J'ai créé une application Web qui utilise les méthodes history
pushState
et replaceState
afin de parcourir les pages tout en mettant à jour l'historique.
Le script lui-même fonctionne presque parfaitement; il chargera les pages correctement et générera des erreurs de page lorsqu'elles doivent être lancées. Cependant, j'ai remarqué un problème étrange où pushState
poussera plusieurs entrées en double (et remplacera les entrées avant) dans l'historique.
Par exemple, disons que je fais ce qui suit (dans l'ordre):
Charger index.php (l'historique sera: Index)
Accédez à profile.php (l'historique sera: Profile, Index)
Accédez à search.php (l'historique sera: Recherche, Recherche, Index)
Accédez à dashboard.php
Enfin, voici ce qui va ressortir de mon histoire (par ordre du plus récent au plus ancien):
Dashboard
Dashboard
Dashboard
Search
Index
Le problème est que lorsqu'un utilisateur clique sur les boutons avant ou arrière, il sera soit redirigé vers la page incorrecte, soit devra cliquer plusieurs fois pour revenir en arrière une fois. Ça, et ça n'aura aucun sens s'ils vont vérifier leur histoire.
Voici ce que j'ai jusqu'à présent:
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);
}
}
});
Toute aide est appréciée,
Cheers.
la source
updateHistory
fonction. Maintenant,updateHistory
vous pouvez être appelé deux fois, d'abord, lorsque vous initialisez Traveler (window.Traveller = new Traveller();
,constructor
->init
->send
->render
->updateHistory
), puis également àredirect
partir declick
eventListener. Je ne l'ai pas testé, juste des devinettes, alors ajoutez-le en tant que commentaire et non en tant que réponse.Réponses:
Avez-vous un gestionnaire onpopstate ?
Si oui, vérifiez-y également si vous ne poussez pas vers l'histoire. Le fait que certaines entrées soient supprimées / remplacées dans la liste d'historique peut être un signe important. En effet, voyez cette réponse SO :
Une fois, j'ai eu exactement le même problème que vous décrivez, mais il a été causé en fait par des allers-retours pour essayer de comprendre le bogue, ce qui pourrait éventuellement déclencher le gestionnaire popState. À partir de ce gestionnaire, il appellera alors history.push. Donc, à la fin, j'avais également des entrées en double et d'autres manquantes, sans aucune explication logique.
J'ai supprimé l'appel à history.push, l'ai remplacé par history.replace après avoir vérifié certaines conditions, et après que cela a fonctionné comme un charme :)
MODIFIER -> CONSEIL
Si vous ne trouvez pas le code qui appelle history.pushState:
Essayez en remplaçant les fonctions history.pushState et replaceState par le code suivant:
Ensuite, chaque fois que les points d'arrêt sont déclenchés, consultez la pile des appels.
Dans la console, si vous souhaitez empêcher un pushState, entrez simplement
allowPush = false;
ouallowReplace = false;
avant de reprendre. De cette façon, vous ne manquerez aucun history.pushState, et pourrez remonter et trouver le code qui l'appelle :)la source