如何通过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
那么,我看到很多取代history
的pushState
属性的例子,但我不知道这是一个好主意,我宁愿创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.pushState
和window.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()