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

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

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

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

更新:

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

更新2:

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


当前回答

除了其他答案。我们可以使用History接口,而不是存储原始函数。

history.pushState = function()
{
    // ...

    History.prototype.pushState.apply(history, arguments);
}

其他回答

除了其他答案。我们可以使用History接口,而不是存储原始函数。

history.pushState = function()
{
    // ...

    History.prototype.pushState.apply(history, arguments);
}

我宁愿不覆盖本机历史方法,所以这个简单的实现创建了我自己的函数,称为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"); 

我曾经用过这个:

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,所以你可能想要把它换行。我不关心这个。)

标准状态如下:

注意,仅仅调用history.pushState()或history.replaceState()不会触发popstate事件。popstate事件仅由浏览器操作触发,例如单击后退按钮(或在JavaScript中调用history.back())

我们需要调用history.back()来触发WindowEventHandlers.onpopstate

所以不是:

history.pushState(...)

do:

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

谢谢@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)
        },
    })
})