如何通过history.pushState获得关于历史变化的通知?

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

可悲的是,这意味着这些呼叫不能再通过onhashchange来检测。

我的问题是:有一个可靠的方法(破解?))来检测网站使用history.pushState ? 规范没有提到任何有关事件的提出(至less我找不到任何东西)。
我试图创build一个门面,并用我自己的JavaScript对象replacewindow.history ,但它根本没有任何效果。

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

更新:

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

更新2:

另一个(方面)的问题是,当使用pushState时, window.location没有被更新(但是我已经在这里读到了关于它的内容)。

5.5.9.1事件定义

浏览会话历史logging时,某些情况下会触发popstate事件。

据此,当你使用pushState时候,popstate没有理由被解雇。 但是像pushstate这样的事件会派上用场。 因为history是一个主机对象,所以你应该小心,但是Firefox在这种情况下似乎很好。 这段代码工作得很好:

 (function(history){ var pushState = history.pushState; history.pushState = function(state) { if (typeof history.onpushstate == "function") { history.onpushstate({state: state}); } // ... whatever else you want to do // maybe call onhashchange e.handler return pushState.apply(history, arguments); }; })(window.history); 

你的jsfiddle 变成

 window.onpopstate = history.onpushstate = function(e) { ... } 

你可以用相同的方法来修改window.history.replaceState

注意:当然你可以添加onpushstate到全局对象,甚至可以通过add/removeListener来处理更多的事件

你可以绑定到window.onpopstate事件?

https://developer.mozilla.org/en/DOM%3awindow.onpopstate

从文档:

窗口上的popstate事件的事件处理程序。

每当活动的历史logging条目发生变化时,popstate事件就会发送到窗口。 如果被激活的历史logging是通过调用history.pushState()创build的,或者被history.replaceState()调用影响,则popstate事件的state属性包含历史logging条目状态对象的副本。

我曾经使用这个:

 var _wr = function(type) { var orig = history[type]; return function() { var rv = orig.apply(this, arguments); var e = new Event(type); e.arguments = arguments; window.dispatchEvent(e); return rv; }; }; history.pushState = _wr('pushState'), history.replaceState = _wr('replaceState'); window.addEventListener('replaceState', function(e) { console.warn('THEY DID IT AGAIN!'); }); 

这几乎和galambalazs一样。

这通常是过度杀伤。 它可能不适用于所有浏览器。 (我只关心我的浏览器版本。)

(它留下一个var _wr ,所以你可能想包装它或什么,我不在乎这一点。)

既然你在问一个Firefox的插件,那么这个代码就是我的工作。 不推荐使用unsafeWindow ,并且在修改后从客户端脚本调用pushState时发生错误:

权限被拒绝访问属性history.pushState

相反,有一个名为exportFunction的API,它允许将函数注入到window.history如下所示:

 var pushState = history.pushState; function pushStateHack (state) { if (typeof history.onpushstate == "function") { history.onpushstate({state: state}); } return pushState.apply(history, arguments); } history.onpushstate = function(state) { // callback here } exportFunction(pushStateHack, unsafeWindow.history, {defineAs: 'pushState', allowCallbacks: true}); 

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

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

从一个framescript(对于e10的兼容性):

 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

和:resource://gre/modules/WebNavigationContent.js

那么,我看到很多取代historypushState属性的例子,但我不知道这是一个好主意,我宁愿创build一个基于类似的API历史的服务事件,你可以控制不仅推状态,但取而代之的是,它为许多其他不依赖于全局历史API的实现打开了大门。 请检查下面的例子:

 function HistoryAPI(history) { EventEmitter.call(this); this.history = history; } HistoryAPI.prototype = utils.inherits(EventEmitter.prototype); const prototype = { pushState: function(state, title, pathname){ this.emit('pushstate', state, title, pathname); this.history.pushState(state, title, pathname); }, replaceState: function(state, title, pathname){ this.emit('replacestate', state, title, pathname); this.history.replaceState(state, title, pathname); } }; Object.keys(prototype).forEach(key => { HistoryAPI.prototype = prototype[key]; }); 

如果您需要EventEmitter定义,则上面的代码基于EventEmitter事件发射器: https : //github.com/nodejs/node/blob/36732084db9d0ff59b6ce31e839450cd91a156be/lib/events.js 。 utils.inherits实现可以在这里find: https : //github.com/nodejs/node/blob/36732084db9d0ff59b6ce31e839450cd91a156be/lib/util.js#L970

galambalazs的答案猴子补丁window.history.pushStatewindow.history.replaceState ,但由于某种原因,它停止为我工作。 这是另一个不太好的因为它使用轮询:

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

作为标准状态:

请注意,只调用history.pushState()或history.replaceState()不会触发popstate事件。 popstate事件只能通过浏览器的操作来触发,比如点击后退button(或者在JavaScript中调用history.back())。

我们需要调用history.back()来修改WindowEventHandlers.onpopstate

所以如下:

 history.pushState(...) 

做:

 history.pushState(...) history.pushState(...) history.back()