pushState erstellt doppelte Verlaufseinträge und überschreibt vorherige

15

Ich habe ein Web - App erstellt , die die nutzen history pushStateund replaceStatewährend Methoden navigieren , um durch die Seiten auch die Geschichte zu aktualisieren.

Das Skript selbst funktioniert fast perfekt; Es lädt Seiten korrekt und wirft Seitenfehler aus, wenn sie geworfen werden müssen. Ich habe jedoch ein seltsames Problem festgestellt pushState, bei dem mehrere doppelte Einträge in den Verlauf verschoben (und Einträge davor ersetzt) ​​werden.

Nehmen wir zum Beispiel an, ich mache Folgendes (in der richtigen Reihenfolge):

  1. Laden Sie index.php hoch (Verlauf lautet: Index)

  2. Navigieren Sie zu profile.php (Verlauf lautet: Profil, Index)

  3. Navigieren Sie zu search.php (Verlauf lautet: Suchen, Suchen, Indexieren)

  4. Navigieren Sie zu dashboard.php

Dann ist es schließlich das, was in meiner Geschichte auftauchen wird (in der Reihenfolge der letzten bis ältesten):

Armaturenbrett
Armaturenbrett
Armaturenbrett Suchindex


Das Problem dabei ist, dass ein Benutzer, wenn er auf die Vorwärts- oder Rückwärtsschaltflächen klickt, entweder auf die falsche Seite umgeleitet wird oder mehrmals klicken muss, um einmal zurück zu gehen. Das, und es macht keinen Sinn, wenn sie ihre Geschichte überprüfen.

Das habe ich bisher:

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);
        }
    }
});

Jede Hilfe wird geschätzt,
Prost.

GROVER.
quelle
Sie möchten also nur dann eine Verlaufsänderung weitergeben, wenn sich das Ziel vom letzten Eintrag unterscheidet?
Aluan Haddad
Geschieht dies scheinbar zufällig? Oder sind bestimmte Seiten immer diejenigen, die doppelte Verlaufseinträge verursachen.
SamVK
@AluanHaddad nein Ich möchte eine Verlaufsänderung nur dann weitergeben, wenn sich der Verlauf ändert (dh wenn ein Benutzer durch die Site navigiert). Ähnlich wie in einer normalen Situation ... (Der Übergang vom Index zum Profil zur Suche in StackOverflow würde genau das in meinem Verlauf zurückgeben)
GROVER.
@SamVK Es spielt keine Rolle, auf welcher Seite sich die Einträge nach dem Navigieren von der ersten Ladeseite (dh index.php) selbst duplizieren.
GROVER.
1
Nun, ich bin mir nicht ganz sicher, also schreibe ich in Kommentaren. Ich sehe, dass Sie Browserverlauf Dinge in Ihre updateHistoryFunktion hinzufügen . Nun updateHistorykann zweimal genannt zu werden , 1., wenn Sie die Initialisierung von Reisenden ( window.Traveller = new Traveller();, constructor-> init-> send-> render-> updateHistory), dann auch redirectvon der clickeventlistener. Ich habe es nicht getestet, nur wild geraten, also füge es als Kommentar und nicht als Antwort hinzu.
Akshit Arora

Antworten:

3

Haben Sie einen Onpopstate- Handler?

Wenn ja, dann überprüfen Sie dort auch, ob Sie nicht auf die Geschichte drängen. Dass einige Einträge in der Verlaufsliste entfernt / ersetzt werden, könnte ein großes Zeichen sein. In der Tat sehen Sie diese SO Antwort :

Beachten Sie, dass history.pushState () einen neuen Status als neuesten Verlaufsstatus festlegt. Und window.onpopstate wird aufgerufen, wenn Sie zwischen den von Ihnen festgelegten Zuständen navigieren (vorwärts / rückwärts).

Drücken Sie also nicht pushState, wenn window.onpopstate aufgerufen wird, da hierdurch der neue Status als letzter Status festgelegt wird und dann nichts mehr zu tun ist.

Ich hatte einmal genau das gleiche Problem wie Sie beschrieben, aber es wurde tatsächlich dadurch verursacht, dass ich hin und her ging, um zu versuchen, den Fehler zu verstehen, der schließlich den popState-Handler auslösen würde. Von diesem Handler würde es dann history.push aufrufen. Am Ende hatte ich auch einige doppelte Einträge und einige fehlten ohne logische Erklärung.

Ich habe den Aufruf von history.push entfernt, ihn durch einen history.replace ersetzt, nachdem ich einige Bedingungen überprüft hatte und nachdem er wie ein Zauber funktioniert hat :)

BEARBEITEN -> TIPP

Wenn Sie nicht finden können, welcher Code history.pushState aufruft:

Überschreiben Sie die Funktionen history.pushState und replaceState mit dem folgenden Code:

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);
    }
}

Schauen Sie sich dann jedes Mal, wenn die Haltepunkte ausgelöst werden, den Aufrufstapel an.

Wenn Sie in der Konsole einen pushState verhindern möchten, geben Sie einfach allowPush = false;oder ein, allowReplace = false;bevor Sie fortfahren . Auf diese Weise verpassen Sie keine history.pushState und können den Code finden, der ihn aufruft :)

Bonjour123
quelle
Ich tue es, aber es
pusht
Sehr seltsam. Versuchen Sie, die Funktion history.push mit dem folgenden Code zu überschreiben: const hP = history.pushState history.pushState = (loc) => {debugger; hP (loc)} und machen Sie dasselbe für history.replaceState. Dann schauen Sie jedes Mal, wenn die Haltepunkte ausgelöst werden, auf den Aufrufstapel
Bonjour123,
Ich habe den Aufrufstapel überprüft und alles scheint korrekt zu sein - aber der Verlauf möchte nicht funktionieren. Warum muss Mozilla alles so schwierig machen :(
GROVER.
Danke :) Und wenn Sie Chrome anprobieren, welche Ergebnisse haben Sie?
Bonjour123