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

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

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

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

更新:

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

更新2:

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


当前回答

你可以绑定到窗户上。onpopstate事件吗?

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

从文档中可以看出:

popstate的事件处理程序 事件。 对象的popstate事件被分派到 窗口每次激活历史记录 输入的变化。如果历史记录条目 被激活是由呼叫创建的 到history.pushState()或被影响 通过调用history.replaceState(), popstate事件的状态属性 包含历史记录项的副本 状态对象。

其他回答

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);
})();

你可以绑定到窗户上。onpopstate事件吗?

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

从文档中可以看出:

popstate的事件处理程序 事件。 对象的popstate事件被分派到 窗口每次激活历史记录 输入的变化。如果历史记录条目 被激活是由呼叫创建的 到history.pushState()或被影响 通过调用history.replaceState(), popstate事件的状态属性 包含历史记录项的副本 状态对象。

谢谢@KalanjDjordjeDjordje的回答。我试图让他的想法成为一个完整的解决方案:

const onChangeState = (state, title, url, isReplace) => { 
    // define your listener here ...
}

// set onChangeState() listener:
['pushState', 'replaceState'].forEach((changeState) => {
    // store original values under underscored keys (`window.history._pushState()` and `window.history._replaceState()`):
    window.history['_' + changeState] = window.history[changeState]
    
    window.history[changeState] = new Proxy(window.history[changeState], {
        apply (target, thisArg, argList) {
            const [state, title, url] = argList
            onChangeState(state, title, url, changeState === 'replaceState')
            
            return target.apply(thisArg, argList)
        },
    })
})

既然你问的是Firefox插件,下面是我要使用的代码。不再推荐使用unsafeWindow,并且当pushState被修改后从客户端脚本调用时会出现错误:

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

相反,有一个名为exportFunction的API,它允许将函数注入到window中。历史是这样的:

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});

我不认为这是一个好主意,即使你可以修改本机函数,你应该始终保持你的应用程序范围,所以一个好的方法是不使用全局pushState函数,而是使用你自己的一个:

function historyEventHandler(state){ 
    // your stuff here
} 

window.onpopstate = history.onpushstate = historyEventHandler

function pushHistory(...args){
    history.pushState(...args)
    historyEventHandler(...args)
}
<button onclick="pushHistory(...)">Go to happy place</button>

请注意,如果任何其他代码使用本机pushState函数,则不会获得事件触发器(但如果发生这种情况,则应该检查代码)