pushState creando voci duplicate della cronologia e sovrascrivendo quelle precedenti


15

Ho creato un'app Web che utilizza i metodi history pushStatee replaceStateper navigare tra le pagine e allo stesso tempo aggiornare la cronologia.

La sceneggiatura stessa funziona quasi perfettamente; caricherà le pagine correttamente e genererà errori di pagina quando devono essere lanciati. Tuttavia, ho notato uno strano problema in cui pushStatespingerà più voci duplicate (e sostituirà le voci precedenti) nella cronologia.

Ad esempio, supponiamo che io faccia quanto segue (in ordine):

  1. Carica index.php (la cronologia sarà: Indice)

  2. Vai a profile.php (la cronologia sarà: Profilo, Indice)

  3. Vai a search.php (la cronologia sarà: Cerca, Cerca, Indice)

  4. Vai a dashboard.php

Quindi finalmente, questo è ciò che verrà nella mia storia (in ordine dal più recente al più vecchio):

Dashboard
Dashboard Indice di ricerca del
dashboard


Il problema è che quando un utente fa clic sui pulsanti avanti o indietro, verrà reindirizzato alla pagina errata o dovrà fare clic più volte per tornare indietro una volta. Quello, e non ha senso se vanno a controllare la loro storia.

Questo è quello che ho finora:

var Traveller = function(){
    this._initialised = false;

    this._pageData = null;
    this._pageRequest = null;

    this._history = [];
    this._currentPath = null;
    this.abort = function(){
        if(this._pageRequest){
            this._pageRequest.abort();
        }
    };
    // initialise traveller (call replaceState on load instead of pushState)
    return this.init();
};

/*1*/Traveller.prototype.init = function(){
    // get full pathname and request the relevant page to load up
    this._initialLoadPath = (window.location.pathname + window.location.search);
    this.send(this._initialLoadPath);
};
/*2*/Traveller.prototype.send = function(path){
    this._currentPath = path.replace(/^\/+|\/+$/g, "");

    // abort any running requests to prevent multiple
    // pages from being loaded into the DOM
    this.abort();

    return this._pageRequest = _ajax({
        url: path,
        dataType: "json",
        success: function(response){
            // render the page to the dom using the json data returned
            // (this part has been skipped in the render method as it
            // doesn't involve manipulating the history object at all
            window.Traveller.render(response);
        }
    });
};
/*3*/Traveller.prototype.render = function(data){
    this._pageData = data;
    this.updateHistory();
};
/*4*/Traveller.prototype.updateHistory = function(){
    /* example _pageData would be:
    {
        "page": {
            "title": "This is a title",
            "styles": [ "stylea.css", "styleb.css" ],
            "scripts": [ "scripta.js", "scriptb.js" ]
        }
    }
    */
    var state = this._pageData;
    if(!this._initialised){
        window.history.replaceState(state, state.title, "/" + this._currentPath);
        this._initialised = true;
    } else {
        window.history.pushState(state, state.title, "/" + this._currentPath);  
    }
    document.title = state.title;
};

Traveller.prototype.redirect = function(href){
    this.send(href);
};

// initialise traveller
window.Traveller = new Traveller();

document.addEventListener("click", function(event){
    if(event.target.tagName === "a"){
        var link = event.target;
        if(link.target !== "_blank" && link.href !== "#"){
            event.preventDefault();
            // example link would be /profile.php
            window.Traveller.redirect(link.href);
        }
    }
});

Tutto l'aiuto è apprezzato,
Saluti.


Quindi vuoi propagare una modifica della cronologia solo se la destinazione è diversa dall'ultima voce?
Aluan Haddad,

Sta accadendo apparentemente in modo casuale? Oppure le pagine specifiche sono sempre quelle che causano voci duplicate nella cronologia.
SamVK,

@AluanHaddad no Voglio solo propagare un cambio di cronologia se c'è un cambio di cronologia (cioè quando un utente sta navigando attraverso il sito). Simile a ciò che accadrebbe in una situazione normale ... (passare da un indice all'altro per cercare StackOverflow restituirebbe esattamente questo nella mia storia)
GROVER.

@SamVK Non importa quale pagina, dopo aver navigato dalla pagina di caricamento iniziale (es. Index.php), le voci si duplicheranno da sole.
GROVER.

1
Beh, non ne sono del tutto sicuro, quindi scrivendo nei commenti. Vedo che stai aggiungendo elementi della cronologia del browser nella tua updateHistoryfunzione. Ora, updateHistorypossono essere sempre chiamato due volte, prima, quando si sta inizializzazione viaggiatore ( window.Traveller = new Traveller();, constructor-> init-> send-> render-> updateHistory), poi anche dal redirectdal clickeventListener. Non l'ho provato, ho solo indovinato, quindi aggiungendolo come commento e non come risposta.
Akshit Arora,

Risposte:


3

Hai un gestore onpopstate ?

Se sì, controlla anche lì se non stai spingendo alla cronologia. Che alcune voci vengano rimosse / sostituite nell'elenco cronologico potrebbe essere un grande segno. In effetti, vedi questa risposta SO :

Tieni presente che history.pushState () imposta un nuovo stato come stato della cronologia più recente. E window.onpopstate viene chiamato durante la navigazione (indietro / avanti) tra gli stati impostati.

Quindi non spingereState quando viene chiamato window.onpopstate, in quanto questo imposterà il nuovo stato come ultimo stato e quindi non c'è nulla da fare.

Una volta ho avuto esattamente lo stesso problema che descrivi, ma in realtà è stato causato da me che andavo avanti e indietro per cercare di capire il bug, che alla fine avrebbe innescato il gestore popState. Da quel gestore, avrebbe chiamato history.push. Quindi alla fine, avevo anche alcune voci duplicate e alcune mancanti, senza alcuna spiegazione logica.

Ho rimosso la chiamata a history.push, l'ho sostituita da history.replace dopo aver verificato alcune condizioni e dopo ha funzionato come un fascino :)

MODIFICA -> SUGGERIMENTO

Se non riesci a individuare quale codice sta chiamando history.pushState:

Prova sovrascrivendo le funzioni history.pushState e replaceState con il seguente codice:

window.pushStateOriginal = window.history.pushState.bind(window.history);
window.history.pushState = function () {
    var args = Array.prototype.slice.call(arguments, 0);
    let allowPush  = true;
    debugger;
    if (allowPush ) {
        window.pushStateOriginal(...args);
    }
}
//the same for replaceState
window.replaceStateOriginal = window.history.replaceState.bind(window.history);
window.history.replaceState = function () {
    var args = Array.prototype.slice.call(arguments, 0);
    let allowReplace  = true;
    debugger;
    if (allowReplace) {
        window.replaceStateOriginal(...args);
    }
}

Quindi ogni volta che i punti di interruzione vengono attivati, dai un'occhiata allo stack di chiamate.

Nella console, se si desidera impedire un pushState, è sufficiente inserire allowPush = false;o allowReplace = false;prima di riprendere. In questo modo, non ti perderai nessun history.pushState e puoi salire e trovare il codice che lo chiama :)


Lo faccio, ma non spinge o sostituisce affatto la cronologia: / rende solo la pagina
GROVER.

Molto strano. Prova a sovrascrivere la funzione history.push con il seguente codice: const hP = history.pushState history.pushState = (loc) => {debugger; hP (loc)} e fare lo stesso per history.replaceState. Quindi ogni volta che i punti di interruzione vengono attivati, dai un'occhiata allo stack di chiamate
Bonjour123

Ho controllato lo stack di chiamate e tutto sembra essere stato eseguito correttamente, ma la cronologia non vuole funzionare. Perché Mozilla deve rendere tutto così difficile :(
GROVER.

Grazie :) E se provi su Chrome, quali risultati ottieni?
Bonjour123,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.