web-dev-qa-db-fra.com

fenêtre lier POPSTATE

Compte tenu des éléments suivants:

$(window).bind("popstate", function() {
    alert('popstate');
});

Au premier chargement, l'alerte se déclenche avec FireFox et Chrome mais pas Safari. Pourquoi cela? Quelqu'un d'autre a vu cela et sait comment le résoudre au mieux?

29
AnApprentice

La situation est désormais inversée. Chrome a corrigé le bogue et déclenche maintenant le popstate au chargement de la page mais Firefox 4 (depuis RC) s'est écarté de la spécification et ne déclenche plus le popstate !

[~ # ~] mise à jour [~ # ~] : la spécification HTML5 a été modifiée en 2011 pour indiquer que le popstate ne doit pas être déclenché au chargement de la page. Firefox et Chrome font maintenant la bonne chose à partir de Firefox 4 et Chrome 34 .

15
Tamlyn

Voir le code de pjax. pjax est une bibliothèque open source assez populaire maintenant, donc la logique ci-dessous pourrait être la meilleure pour éviter ce problème.

var popped = ('state' in window.history), initialURL = location.href
$(window).bind('popstate', function(event) {
  // Ignore inital popstate that some browsers fire on page load
  var initialPop = !popped && location.href == initialURL
  popped = true
  if ( initialPop ) return
  ...

https://github.com/defunkt/jquery-pjax/blob/master/jquery.pjax.js

45
Nobu

Dans webkit, l'événement popstate est déclenché au chargement de la page. Pour éviter cela, cette solution simple fonctionne pour moi:

Chaque fois que je déclenche history.Push(...) j'ajoute le classhistorypushed à la balise body-:

history.Push(...);
$("body").addClass("historypushed");

Lorsque je déclenche l'événement popstate, je vérifie cette classe:

$(window).bind('popstate', function(e) {
 if($("body").hasClass("historypushed")) { 
  /* my code */ 
 } 
});
17
lohsie

Un moyen simple d'éviter ce problème consiste à définir le premier argument de pushState sur true, puis à vérifier par rapport à onpopstate. Similaire à l'exemple pjax mais un peu plus simple. L'exemple ci-dessous exécutera doSomething () pour chaque événement popstate à l'exception du premier chargement de page.

function setupPopState() {
  if (history.popState) {
    # immediately replace state to ensure popstate works for inital page
    history.replaceState(true, null, window.location.pathname);

    $(window).bind('popstate', function(event) {
      if (event.originalEvent.state) {
        doSomething();
      }
    });
  }
}
3
HuffMoody

Il y avait un bogue dans Webkit qui implémentait incorrectement l'événement "popstate". Consultez ce post simple expliquant le problème (petit show et tell cool): http://www.bcherry.net/playground/pushstate

Ma suggestion serait d'implémenter votre propre tracker d'événement "popstate" pour Safari. Quelque chose comme ça:

$(window).load(function(){
  function fire_popstate(){
    $(this).trigger("popstate"); // fire it when the page first loads
  }
  var lasthash = window.location.hash;
  setInterval(function(){
    var currenthash = window.location.hash;
    if(lasthash != currenthash){
      fire_popstate();
    }
  }, 500);//check every half second if the url has changed
});

Vous pouvez envelopper cette déclaration dans un test de navigateur pour vérifier le safari. Mieux encore, voyez si "popstate" a été déclenché au moment où le DOM est prêt, puis appliquez la fonction interne pour remplacer l'implémentation. La seule chose que vous ne voulez pas, c'est d'avoir deux événements popstate à déclencher (duplication de la logique de votre gestionnaire d'événements, excellent moyen de verrouiller l'interface utilisateur).

2
Shakakai

Ceci est ma solution de contournement.

window.setTimeout(function() {
  window.addEventListener('popstate', function() {
    // ...
  });
}, 1000);
0
Baggz

Cette réponse à une question similaire suggère de vérifier la vérité booléenne de event.state dans le gestionnaire d'événements popstate:

window.addEventListener('popstate', function(event) {
    if (event.state) {
        alert('!');
    }
}, false);

Vous pouvez également lier votre fonction de rappel à un événement popstate comme ceci:

window.onpopstate = callback();

Consultez ici pour plus d'informations sur cette solution

0
Wilt

Je convertis les réponses @Nobu & @Tamlyn en un objet, j'ajoute également un petit correctif en ajoutant "window.history.state! == null". Dans certains navigateurs, history.state existe, mais il est nul, donc cela ne fonctionnait pas.

/**
 * The HTML5 spec was changed in 2011 to state popstate should not 
 * fired on page load. Chrome(34) & Firefox(4) has fixed the bug but 
 * some browsers (e.g. Safari 5.1.7) are still fire the popstate on
 * the page load. This object created from the Pjax Library to handle 
 * this issue.
 */
var popstatePageloadFix = {
        popped : ('state' in window.history && window.history.state !== null),
        initialUrl : location.href,
        initialPop : false,
        init : function() {
                this.initialPop = !this.popped && location.href == this.initialUrl;
                this.popped = true;
                return this.initialPop;
        }
};

$(window).on("popstate", function (event) {
        
        // Ignore initial popstate that some browsers fire on page load
        if ( popstatePageloadFix.init() ) return;
        
        ...

});

Merci @Nobu! Merci @Tamlyn!

0
Roy Shoa