Stworzyłem aplikację internetową, która korzysta z metod history pushStatei 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 pushStatewypychaniu 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.
updateHistoryfunkcji. Teraz updateHistorymoż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ż redirectz poziomu clickeventListener. Nie testowałem tego, tylko zgadywanie, więc dodając go jako komentarz, a nie odpowiedź.