在Chrome中加载页面上的Popstate

spl*_*ter 94 html5 google-chrome javascript-events browser-history

我正在为我的网络应用程序使用History API,但有一个问题.我执行Ajax调用以更新页面上的一些结果并使用history.pushState()来更新浏览器的位置栏而不重新加载页面.然后,当然,我使用window.popstate来恢复单击后退按钮时的先前状态.

问题是众所周知的 - Chrome和Firefox以不同的方式对待popstate事件.虽然Firefox没有在第一次加载时启动它,但Chrome确实如此.我想拥有Firefox风格并且不会在加载时触发事件,因为它只是在加载时使用完全相同的结果更新结果.除了使用History.js之外是否有解决方法?我不想使用它的原因是 - 它本身需要太多的JS库,因为我需要在已经有太多JS的CMS中实现它,我想最小化JS我放入它.

所以,想知道是否有办法让Chrome不加载'popstate'加载,或者有人试图使用History.js,因为所有库一起混合成一个文件.

Pav*_*sch 49

在版本19的Google Chrome中,@ spliter的解决方案停止了工作.正如@johnnymire指出的那样,Chrome 19中的history.state存在,但它是null.

我的解决方法是将window.history.state!== null添加到检查window.history中是否存在状态:

var popped = ('state' in window.history && window.history.state !== null), initialURL = location.href;
Run Code Online (Sandbox Code Playgroud)

我在所有主流浏览器和Chrome版本19和18中进行了测试.它看起来很有效.

  • 这在Chrome 21中不再适用于我.我在页面加载时将弹出设置为false,然后在任何推送或弹出时将弹出设置为true.这首网络Chrome首次加载.这里解释得更好:https://github.com/defunkt/jquery-pjax/issues/143#issuecomment-6194330 (19认同)
  • 谢谢你的提醒; 我认为你可以用`!= null`来简化`in`和`null`检查,因为它也会处理`undefined`. (5认同)
  • 只有在`history.pushState()`方法(如`history.pushState(null,null,'http // example.com /)`)中始终使用`null`时,才能使用它.当然,这可能是最常用的(这就是它在jquery.pjax.js和大多数其他演示中的设置).但是如果浏览器实现了`window.history.state`(如FF和Chrome 19+),[window.history.state在刷新时或浏览器重启后可能为非null](https://developer.mozilla.组织/ EN/DOM/Manipulating_the_browser_history#Reading_the_current_state) (5认同)

Tor*_*ben 30

如果您不想对添加到onpopstate的每个处理程序采取特殊措施,我的解决方案可能对您有意义.此解决方案的一大优点还在于,可以在页面加载完成之前处理onpopstate事件.

只需在添加任何onpopstate处理程序之前运行此代码,一切都应该按预期工作(也就像在Mozilla ^^中).

(function() {
    // There's nothing to do for older browsers ;)
    if (!window.addEventListener)
        return;
    var blockPopstateEvent = document.readyState!="complete";
    window.addEventListener("load", function() {
        // The timeout ensures that popstate-events will be unblocked right
        // after the load event occured, but not in the same event-loop cycle.
        setTimeout(function(){ blockPopstateEvent = false; }, 0);
    }, false);
    window.addEventListener("popstate", function(evt) {
        if (blockPopstateEvent && document.readyState=="complete") {
            evt.preventDefault();
            evt.stopImmediatePropagation();
        }
    }, false);
})();
Run Code Online (Sandbox Code Playgroud)

这个怎么运作:

Chrome,Safari和可能的其他webkit浏览器在加载文档时触发onpopstate事件.这不是预期的,因此我们阻止popstate事件,直到文档加载后的第一个事件循环cicle.这是通过preventDefault和stopImmediatePropagation调用完成的(与stopPropagation不同,stopImmediatePropagation会立即停止所有事件处理程序调用).

但是,由于当Chrome错误地触发时,文档的readyState已经"完成",我们允许opopstate事件,在文档加载完成之前已经触发,以允许在加载文档之前进行onpopstate调用.

更新2014-04-23:修复了在加载页面后执行脚本时阻止popstate事件的错误.

  • 迄今为止我的最佳解决方案.我多年来一直在使用setTimeout方法,但是当页面加载缓慢/用户非常快速地触发pushstate时,它会导致错误的行为.如果设置了状态,则"window.history.state"会在重新加载时失败.在pushState混乱代码之前设置变量.您的解决方案非常优雅,可以放在其他脚本之上,而不会影响其余的代码库.谢谢. (4认同)
  • **这是解决方案!**如果我在几天前尝试解决这个问题时只读了这篇文章.这是唯一一个完美运作的人.谢谢! (4认同)

小智 28

仅使用setTimeout不是正确的解决方案,因为您不知道要加载内容需要多长时间,因此可能会在超时后发出popstate事件.

这是我的解决方案:https: //gist.github.com/3551566

/*
* Necessary hack because WebKit fires a popstate event on document load
* https://code.google.com/p/chromium/issues/detail?id=63040
* https://bugs.webkit.org/process_bug.cgi
*/
window.addEventListener('load', function() {
  setTimeout(function() {
    window.addEventListener('popstate', function() {
      ...
    });
  }, 0);
});
Run Code Online (Sandbox Code Playgroud)

  • 甜!比最受欢迎和/或接受的答案更好! (3认同)

spl*_*ter 25

该解决方案已在jquery.pjax.js第195-225行中找到:

// Used to detect initial (useless) popstate.
// If history.state exists, assume browser isn't going to fire initial popstate.
var popped = ('state' in window.history), initialURL = location.href


// popstate handler takes care of the back and forward buttons
//
// You probably shouldn't use pjax on pages with other pushState
// stuff yet.
$(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

  var state = event.state

  if ( state && state.pjax ) {
    var container = state.pjax
    if ( $(container+'').length )
      $.pjax({
        url: state.url || location.href,
        fragment: state.fragment,
        container: container,
        push: false,
        timeout: state.timeout
      })
    else
      window.location = location.href
  }
})
Run Code Online (Sandbox Code Playgroud)

  • 这个解决方案在Windows(Chrome 19)上停止了我的工作,仍在使用Mac(Chrome 18).看起来像history.state存在于Chrome 19但不是18. (8认同)
  • 对于任何寻求解决方案的人来说,Torben的答案就像当今Chrome和Firefox版本的魅力一样 (5认同)

Tom*_*zie 14

比重新实现pjax更直接的解决方案是在pushState上设置变量,并在popState上检查变量,因此初始popState不会在加载时不一致地触发(不是jquery特定的解决方案,只是将它用于事件):

$(window).bind('popstate', function (ev){
  if (!window.history.ready && !ev.originalEvent.state)
    return; // workaround for popstate on load
});

// ... later ...

function doNavigation(nextPageId) {
  window.history.ready = true;

  history.pushState(state, null, 'content.php?id='+ nextPageId); 
  // ajax in content instead of loading server-side
}
Run Code Online (Sandbox Code Playgroud)

  • 这应该是IMO的答案,我尝试了Pavels解决方案,但它在Firefox中无法正常工作 (3认同)