web-dev-qa-db-fra.com

window.onpopstate, event.state == null?

J'ai lu plusieurs questions/réponses sur Stack Overflow et recherché sur Google, je n'arrive pas à obtenir le event.state pour revenir avec autre chose que null.

J'ai compris que cela ne fonctionnera pas avec l'événement jQuery, mais quand je fais quelque chose comme ça:

window.onpopstate = function (e) {
     console.log(e.state)
}

Avec quelque chose dans le sens d'un succès jQuery.ajax appelant

history.pushState({ lastUrl: data.State }, data.Title, data.UrlKey);

Ni Chrome (v19) ni Firefox (v12) ne renvoient autre chose que null? Est-ce que je manque quelque chose? Est-ce que ce n'est pas complètement implémenté?

39
mihok

e.state fait référence à l'avant-dernier état poussé. Vous devez avoir poussé l'état au moins deux fois pour e.state ne pas être null. Cela est dû au fait que vous devez enregistrer l'état lors du premier chargement de votre site, puis à chaque changement d'état.

60
Jaco Briers

Je pense que cette question nécessite une explication plus claire car la phrase "avant-dernier état poussé" dans les autres réponses peut prêter à confusion.

Lorsque nous faisons history.pushState, nous poussons dans la pile d'historique un nouvel état, qui devient celui actuel (comme nous pouvons le voir dans l'url de la barre de navigation).

L'événement window.onpopstate signifie que l'état de l'historique supérieur est sorti (retiré de la pile), de sorte que l'e.state pointe désormais vers le nouvel état supérieur de la pile (comme la barre de navigation pointe vers l'ancien). url).

15
MQ87

J'ai lutté avec ce même problème pendant plusieurs heures. Trop d'heures. L'argument state de onpopstate -handler était null même si j'avais appelé pushState () plusieurs fois avant de cliquer sur le bouton de retour.

J'ai également observé que mon gestionnaire onclick qui appelait pushState () provoquait le déclenchement du gestionnaire onpopstate. Je crois que l'onpopstate -handler ne devrait être appelé que si l'utilisateur clique sur le bouton de retour de ce que j'ai lu sur le Web. Mais cela ne semblait pas être le cas dans mon cas.

Pourrait-il y avoir un bug dans le navigateur? Semble peu probable car j'ai eu le même problème ou un problème similaire sur les deux Chrome et FireFox. Mais possible. Ou peut-être qu'il y a un "bug dans la spécification" trop compliqué à implémenter correctement. Aucune unité publique- tests montrant comment cela devrait fonctionner.

Enfin, je suis arrivé à une solution après quoi TOUT a commencé à travailler. C'était pour mettre ces deux appels dans mon gestionnaire de charge:

pushState (myInitialState, null, href);
pushState (myInitialState, null, href);

Je dois donc faire le même Push-state () appeler TWICE dans le onload-handler! Après cela, mon gestionnaire onpopstate a commencé à obtenir des arguments dont l'état n'était PAS nul mais un état que j'avais précédemment passé comme argument à pushState ().

Je ne comprends pas vraiment pourquoi cela fonctionne maintenant et pourquoi il ne l'a pas fait plus tôt lorsque j'ai appelé pushState SEULEMENT UNE FOIS.

Je voudrais comprendre pourquoi cela fonctionne maintenant, mais j'ai déjà passé trop de temps à le faire fonctionner. Si quelqu'un a une référence à un bon exemple de code en ligne qui l'explique, ce serait génial.

2
Panu Logic

Il sera utile si vous avez déjà entendu parler de la pile annuler/rétablir, comme le montre l'image ci-dessous.

enter image description here

En fait, l'historique est conservé dans une pile comme celle-ci. Votre état actuel provient de l'élément de pile actuel, et supposons qu'il y ait un pointeur vers lui; lorsque vous appelez history.back(), le pointeur va sur un élément plus ancien de la pile, l'état de l'événement onpopstate est le même que history.state soit dit en passant. Vous obtiendrez simplement l'élément vers lequel le pointeur pointe actuellement, qui a été modifié par history.replaceState Ou ajouté par history.pushState.

Ce processus explique également pourquoi history.length Ne change pas, car il représente la longueur de la pile entière. Lorsque vous appelez history.go(1), le pointeur revient à l'élément le plus récent.

Lorsque le pointeur se trouve au milieu de la pile, l'appel de history.pushState Entraînera le saut de tous les éléments au-dessus du pointeur, et un nouvel ajout, vous pouvez également voir les modifications de history'length.

1
W.Leto

Comme Jaco Briers l'a mentionné, pour que popstate fonctionne comme nous le pensons, nous devons d'abord stocker l'état initial auquel revenir lorsque vous cliquez sur le bouton Précédent du navigateur. Voici un exemple d'implémentation utilisant jQuery:

Exemple de code

$(window).on({
  // Store initial state
  'load': function() {
    window.history.pushState({
      'html': '<div id="ajax-content">' + $('#ajax-content').html() + '</div>'
    }, '', document.URL);
  },
  // Handle browser Back/Forward button
  'popstate': function(e) {
    var oState = e.originalEvent.state;
    if (oState) {
      $('#ajax-content').replaceWith(oState.html);
    }
  }
});

...

// Update AJAX content
var sUrl = 'https://www.example.com/category?id=123&format=ajax';
$.ajax({
  type: 'GET',
  url: sUrl,
  success: function(sHtml) {
    $('#ajax-content').replaceWith(sHtml);
    window.history.pushState({
      'html': sHtml
    }, '', sUrl);
  }
});
0
thdoan