窗口绑定POPSTATE

29

给定以下内容:

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

首次加载时,Firefox和Chrome会触发警报,但Safari不会。为什么会这样?还有其他人遇到这个问题并知道如何解决吗?

8个回答

45

查看pjax的代码。pjax现在是相当流行的开源库,因此采用下面的逻辑可能是避免这个问题的最佳方法。

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


16

在webkit中,popstate事件会在页面加载时触发。为了避免这种情况,我使用以下简单的方法:

每次执行history.push(...)时,我都会将类historypushed添加到body标签中:

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

当我触发popstate事件时,我会检查这个类:

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

15

现在情况已经反转。Chrome已经修复了这个bug,现在在页面加载时会触发popstate事件,但是自从RC以来,Firefox 4已经偏离了规范,并且不再触发popstate事件

更新:HTML5规范在2011年进行了更改,指出popstate不应该在页面加载时触发。自从Firefox 4和Chrome 34 发布以来,它们都做到了正确的事情。


2
HTML5规范在2011年进行了修改,规定popstate不应在页面加载时触发。提升https://code.google.com/p/chromium/issues/detail?id=63040是让Chrome重新符合标准的最佳方式。谷歌已经知道Chrome的行为不正确已经两年了。 - Dave

3

避免这个问题的简单方法是将pushState的第一个参数设置为true,然后根据onpopstate进行检查。与pjax示例类似,但更加直观。以下示例将在每个popstate事件中运行doSomething(),除了第一次页面加载。

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();
      }
    });
  }
}

2

Webkit曾经存在一个错误,它没有正确实现"popstate"事件。查看这篇简单的文章,解释了这个问题(很酷的小展示):http://www.bcherry.net/playground/pushstate

我的建议是为Safari实现自己的"popstate"事件跟踪器。可以像这样操作:

$(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
});

您可以将该语句包裹在浏览器测试中以检查Safari是否存在。更好的方法是,在DOM准备就绪时检查是否已触发“popstate”,然后应用内部函数来替换实现。唯一不希望发生的事情就是触发两个“popstate”事件(复制您的事件处理程序逻辑,这是锁定UI的好方法)。


1

这个类似问题的答案建议在popstate事件处理程序中检查event.state的布尔真值:

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

您还可以像这样将回调函数绑定到popstate事件:

window.onpopstate = callback();

请点击这里获取有关该解决方案的更多信息


0

这是我的解决方法。

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

3
这是什么?也许你应该解释一下它,而不是到处Hao123张贴它。请注意保持原意并使语言更加通俗易懂。 - Andriy Drozdyuk
2
@drozzy 当然可以!这段代码会在1秒后向popstate事件添加一个新的事件监听器。通过这种方法,popstate事件不会在页面加载时触发。 - Baggz
2
@Baggz,根据Dive into html5的演示1毫秒更为合适。 - Derek 朕會功夫

0

我将@Nobu和@Tamlyn的答案转换为一个对象,同时添加了一个小修复,即添加“window.history.state!== null”。在某些浏览器中,history.state存在,但它是null,因此它无法工作。

/**
 * 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;
 
 ...

});

感谢 @Nobu! 感谢 @Tamlyn!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接