现在HTML5引入了历史。pushState来改变浏览器的历史记录,网站开始与Ajax结合使用,而不是改变URL的片段标识符。

不幸的是,这意味着onhashchange再也不能检测到这些调用了。

我的问题是:有没有可靠的方法(hack?);))来检测网站何时使用history.pushState?规范没有说明引发的任何事件(至少我没有找到任何事件)。 我试图创造一个立面,并替换了窗户。我自己的JavaScript对象的历史,但它根本没有任何影响。

进一步解释:我正在开发一个Firefox插件,它需要检测这些变化并采取相应的行动。 我知道几天前有一个类似的问题,问监听一些DOM事件是否有效,但我宁愿不依赖于它,因为这些事件可以出于许多不同的原因生成。

更新:

这里是一个jsfiddle(使用Firefox 4或Chrome 8),显示onpopstate没有触发pushState被调用(或我做错了什么?请随意改进!)。

更新2:

另一个问题是那个窗口。使用pushState时,location不会更新(但我已经在这里读到这个,所以我认为)。


当前回答

galambalazs的答案monkey补丁window.history. pushstate和window.history。但由于某种原因,它不再为我工作了。这里有一个替代方案,不太好,因为它使用轮询:

(function() {
    var previousState = window.history.state;
    setInterval(function() {
        if (previousState !== window.history.state) {
            previousState = window.history.state;
            myCallback();
        }
    }, 100);
})();

其他回答

我认为这个话题需要一个更现代的解决方案。

我确信nsIWebProgressListener是在当时,我很惊讶没有人提到它。

从一个框架脚本(e10s兼容性):

let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebProgress);
webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_STATE_WINDOW | Ci.nsIWebProgress.NOTIFY_LOCATION);

然后监听onLoacationChange

onLocationChange: function onLocationChange(webProgress, request, locationURI, flags) {
       if (flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT

这显然会捕获所有的pushState。但是有一条评论警告它“也会触发pushState”。所以我们需要做更多的过滤来确保它只是推送状态的东西。

基于:https://github.com/jgraham/gecko/blob/55d8d9aa7311386ee2dabfccb481684c8920a527/toolkit/modules/addons/WebNavigation.jsm#L18

和:资源:/ / gre /模块/ WebNavigationContent.js

galambalazs的答案monkey补丁window.history. pushstate和window.history。但由于某种原因,它不再为我工作了。这里有一个替代方案,不太好,因为它使用轮询:

(function() {
    var previousState = window.history.state;
    setInterval(function() {
        if (previousState !== window.history.state) {
            previousState = window.history.state;
            myCallback();
        }
    }, 100);
})();

基于@gblazex给出的解决方案,如果你想遵循相同的方法,但使用箭头函数,在你的javascript逻辑中遵循下面的例子:

private _currentPath:string;    
((history) => {
          //tracks "forward" navigation event
          var pushState = history.pushState;
          history.pushState =(state, key, path) => {
              this._notifyNewUrl(path);
              return pushState.apply(history,[state,key,path]); 
          };
        })(window.history);

//tracks "back" navigation event
window.addEventListener('popstate', (e)=> {
  this._onUrlChange();
});

然后,实现另一个函数_notifyUrl(url),该函数在当前页面url更新时触发您可能需要的任何必要操作(即使页面根本没有加载)。

  private _notifyNewUrl (key:string = window.location.pathname): void {
    this._path=key;
    // trigger whatever you need to do on url changes
    console.debug(`current query: ${this._path}`);
  }

我宁愿不覆盖本机历史方法,所以这个简单的实现创建了我自己的函数,称为eventtedpush state,它只是调度一个事件并返回history. pushstate()。这两种方式都很好,但我发现这种实现更简洁,因为原生方法将继续像未来开发人员所期望的那样执行。

function eventedPushState(state, title, url) {
    var pushChangeEvent = new CustomEvent("onpushstate", {
        detail: {
            state,
            title,
            url
        }
    });
    document.dispatchEvent(pushChangeEvent);
    return history.pushState(state, title, url);
}

document.addEventListener(
    "onpushstate",
    function(event) {
        console.log(event.detail);
    },
    false
);

eventedPushState({}, "", "new-slug"); 

我用简单的代理来做到这一点。这是原型的替代方案

window.history.pushState = new Proxy(window.history.pushState, {
  apply: (target, thisArg, argArray) => {
    // trigger here what you need
    return target.apply(thisArg, argArray);
  },
});