如何在页面加载时忽略 window.onpopstate?

39

我正在使用 window.onpopstate,但有一件事情让我烦恼:

浏览器在页面加载时会以不同的方式处理popstate事件。Chrome和Safari总是在页面加载时发出popstate事件,但Firefox没有。

来源

我测试了一下,确实在Chrome和Safari 5.1+中,popstate事件会在页面加载时触发,但在Firefox或IE10中不会。

问题是,我想只侦听用户点击“后退”或“前进”按钮(或通过JavaScript更改历史记录)时的popstate事件,而不想在页面加载时执行任何操作。

换句话说,我想区分popstate事件与其他popstate事件中的页面加载

这是我迄今为止尝试过的方法(我正在使用jQuery):

$(function() {
  console.log('document ready');
  
  setTimeout(function() {
    window.onpopstate = function(event) {
      // Do something here
    }, 10);
});

基本上我正在尝试将我的listener函数绑定到popstate,但要晚于页面加载时绑定。

这似乎有效,但我不喜欢这个解决方案。 我如何确保setTimeout选择的超时时间足够大,但又不要太大(因为我不想等待太久)。

我希望有一个更聪明的解决方案!


4
这个问题在Chrome上似乎已经被修复了。 - Pacerier
1
请更新您的问题,因为今天只有Safari出现了这个问题。Chrome似乎已经解决了这个问题。 - Kush
这个错误的 popstate 发生在 onload 处理之后,这意味着1毫秒总是足够推迟 popstate 注册(因为JS没有多线程并按照注册顺序处理推迟的函数)。 - Radek Pech
4个回答

44

popstate 事件处理程序中检查 event.state 的布尔真实性:

window.addEventListener('popstate', function(event) {
    if (event.state) {
        alert('!');
    }
}, false);
为确保此功能可用,请始终在调用history.pushState()history.replaceState()时指定非null状态参数。此外,考虑使用诸如History.js之类的包装库,以在各种浏览器中提供一致的行为。

2
是的,在页面加载时,event.state为空,但如果调用pushState而没有定义状态,它也可能为空,例如:history.pushState(null, null, url); - Tamás Pap
2
因此,始终定义状态或编写包装器类(或使用现有的类)将使这些内容隐式化。HTML5历史记录API的本机实现不一致,因此我们无论如何都不得不做出某些权衡。 - Marat Tanalin
1
是的,我会使用包装类,并且在调用 pushStatereplaceState 时永远不会使用 null 作为状态。请更新您的答案,以便我可以接受它。感谢您的帮助。 - Tamás Pap
澄清一下:请确保调用 history.pushState(state, null, url),不要在第一个参数上使用 null。当 Safari 在页面加载时调用 popstate 时,event.state 将为 nil。这是区分初始页面加载和显式 pushState 调用的方法。 - tothemario
如果我在页面2中使用pushState,那么在触发popstate时,event.state将为null。如果我连续使用pushState 2次,则在返回时会有一个有效的event.state,但用户需要按3次返回按钮才能看到页面1 - user5490177

7
我遇到了类似的问题,我需要验证确保页面已经完全加载。我使用了类似于以下的代码:
var page_loaded = false;    
window.onpopstate = function(event){
    if(!page_loaded){
       page_loaded = true;
       return false;
     }
    //Continue With Your Code
}

我也在考虑这样的解决方案。问题是,窗口加载发生在第一个onpopstate之前,因此page_loaded将始终为真。(JSBin: http://jsbin.com/awidol/1/edit) - Tamás Pap
@TamasPap 如果要解决这个问题,可以拒绝在第一次执行代码时执行它,将page_load更改为true,然后继续执行代码。 - Rosmarine Popcorn
是的,但在Fireworks和IE10中,第一个onpopstate将不会发生。 - Tamás Pap
@TamasPap 然后尝试检测浏览器并填充if条件,以便仅在执行此操作的浏览器上执行。 - Rosmarine Popcorn
我遇到了相同的问题,这是我所做的,虽然不是一个清晰的解决方案,但它99%的时间可以使用: var JUST_LOADED = true; setTimeout(function(){JUST_LOADED=false},399); - Raj Nathani

5

为了响应popstate事件,您需要将一些状态推入会话历史记录。

例如,在文档准备部分添加此行:

history.pushState(null, null, window.location.pathname);

虽然不是最理想的方法,但它可以在Chrome、Firefox和其他浏览器中正常工作。

当用户单击“后退”或“前进”按钮时,事件会正确触发,手动调用history.back()history.forward()history.go()方法时也是如此。每次调用popstate后,您都需要再推送另一个状态才能使其正常工作。

另请参见:


2
今天起,似乎所有浏览器都不再在页面加载时触发popstate事件了:
各个浏览器以前在处理popstate事件时,在页面加载时的行为是不同的。Firefox从未在页面加载时触发popstate事件。Chrome在版本34之前会触发,而Safari在版本10.0之前会触发。
来源:https://developer.mozilla.org/en-US/docs/Web/API/PopStateEvent

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