Stworzyłem aplikację internetową, która korzysta z metod history
pushState
i replaceState
, aby poruszać się po stronach, jednocześnie aktualizując historię.
Sam skrypt działa prawie idealnie; ładuje strony poprawnie i wyrzuca błędy strony, gdy trzeba je wyrzucić. Zauważyłem jednak dziwny problem polegający na pushState
wypychaniu wielu zduplikowanych wpisów (i zastępowaniu wcześniejszych) do historii.
Załóżmy na przykład, że wykonuję następujące czynności (w kolejności):
Załaduj index.php (historia będzie: Indeks)
Przejdź do profile.php (historia będzie: Profil, Indeks)
Przejdź do search.php (historia będzie: Szukaj, Szukaj, Indeks)
Przejdź do dashboard.php
I wreszcie oto, co pojawi się w mojej historii (w kolejności od najnowszych do najstarszych):
Pulpit nawigacyjny
Pulpit
nawigacyjny Indeks
wyszukiwania
Problem polega na tym, że gdy użytkownik kliknie przyciski przewijania do przodu lub do tyłu, albo zostanie przekierowany na niewłaściwą stronę, albo będzie musiał kliknąć kilka razy, aby wrócić raz. To i nie będzie sensu, jeśli pójdą i sprawdzą swoją historię.
Oto co mam do tej pory:
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);
}
}
});
Cała pomoc jest doceniana, na
zdrowie.
źródło
updateHistory
funkcji. TerazupdateHistory
może być wywoływany dwa razy, po pierwsze, podczas inicjowania produktu Traveler (window.Traveller = new Traveller();
,constructor
->init
->send
->render
->updateHistory
), a następnie równieżredirect
z poziomuclick
eventListener. Nie testowałem tego, tylko zgadywanie, więc dodając go jako komentarz, a nie odpowiedź.Odpowiedzi:
Czy masz program obsługi onpopstate ?
Jeśli tak, sprawdź również, czy nie przesuwasz się do historii. To, że niektóre wpisy są usuwane / zastępowane na liście historii, może być dużym znakiem. Rzeczywiście, zobacz tę SO odpowiedź :
Kiedyś miałem dokładnie ten sam problem, co opisałeś, ale tak naprawdę spowodowałem to, że próbowałem zrozumieć błąd, który ostatecznie uruchomiłby program obsługi popState. Z tego modułu obsługi wywołuje następnie history.push. Na koniec miałem też kilka zduplikowanych wpisów, a niektórych brakowało, bez żadnego logicznego wyjaśnienia.
Usunąłem wywołanie history.push, zastąpiłem go history.replace po sprawdzeniu niektórych warunków, a potem zadziałało jak urok :)
EDYCJA -> WSKAZÓWKA
Jeśli nie możesz zlokalizować, który kod wywołuje history.pushState:
Spróbuj, zastępując funkcje history.pushState i replaceState następującym kodem:
Następnie za każdym razem, gdy punkty przerwania są wyzwalane, spójrz na stos wywołań.
Jeśli chcesz uniemożliwić pushState w konsoli, po prostu wpisz
allowPush = false;
luballowReplace = false;
przed wznowieniem. W ten sposób nie przegapisz żadnej history.pushState i możesz przejść w górę i znaleźć kod, który to wywołuje :)źródło