如何检查URL是否在JavaScript中发生了变化?例如,像GitHub这样使用AJAX的网站,会在#符号后附加页面信息,以创建唯一的URL,而无需重新加载页面。检测该URL是否发生变化的最佳方法是什么?

是否再次调用onload事件? 是否有URL的事件处理程序? 或者必须每秒钟检查URL以检测更改?


当前回答

我希望能够添加locationchange事件监听器。经过下面的修改,我们就可以这样做了

window.addEventListener('locationchange', function () {
    console.log('location changed!');
});

相比之下,window.addEventListener('hashchange',() =>{})只会在url中标签后的部分发生变化时触发,而window.addEventListener('popstate',() =>{})并不总是工作。

这种修改类似于Christian的回答,修改了history对象以添加一些功能。

默认情况下,在这些修改之前,有一个popstate事件,但是没有pushstate和replacestate事件。

这将修改这三个函数,以便所有函数都触发一个自定义locationchange事件供您使用,如果您想使用这些事件,还可以推送state和replacestate事件。

这些是修改:

(() => {
    let oldPushState = history.pushState;
    history.pushState = function pushState() {
        let ret = oldPushState.apply(this, arguments);
        window.dispatchEvent(new Event('pushstate'));
        window.dispatchEvent(new Event('locationchange'));
        return ret;
    };

    let oldReplaceState = history.replaceState;
    history.replaceState = function replaceState() {
        let ret = oldReplaceState.apply(this, arguments);
        window.dispatchEvent(new Event('replacestate'));
        window.dispatchEvent(new Event('locationchange'));
        return ret;
    };

    window.addEventListener('popstate', () => {
        window.dispatchEvent(new Event('locationchange'));
    });
})();

注意,我们正在创建一个闭包,将旧函数保存为新函数的一部分,以便在调用新函数时调用它。

其他回答

经过一番研究后编辑:

不知怎么的,我似乎被Mozilla文档上的文档愚弄了。当代码中调用pushState()或replaceState()时,popstate事件(及其回调函数onpopstate)不会被触发。因此,原来的答案并不适用于所有情况。

然而,有一种方法可以绕过这个问题,根据@alpha123对函数进行猴子补丁:

var pushState = history.pushState;
history.pushState = function () {
    pushState.apply(history, arguments);
    fireEvents('pushState', arguments);  // Some event-handling function
};

原来的答案

假设这个问题的标题是“如何检测URL更改”,当你想知道完整路径更改(而不仅仅是哈希锚)时,答案是你可以监听popstate事件:

window.onpopstate = function(event) {
  console.log("location: " + document.location + ", state: " + JSON.stringify(event.state));
};

在Mozilla Docs中引用popstate

目前(2017年1月)全球92%的浏览器支持popstate。

如果没有窗口事件为您工作(在我的例子中不是这样),您还可以使用MutationObserver来查看根元素(非递归地)。

// capture the location at page load
let currentLocation = document.location.href;

const observer = new MutationObserver((mutationList) => {
  if (currentLocation !== document.location.href) {
    // location changed!
    currentLocation = document.location.href;

    // (do your event logic here)
  }
});

observer.observe(
  document.getElementById('root'),
  {
    childList: true,

    // important for performance
    subtree: false
  });

这可能并不总是可行的,但通常情况下,如果URL更改,根元素的内容也会更改。

我没有进行概要分析,但从理论上讲,这比计时器的开销要小,因为Observer模式通常是实现的,因此当发生更改时,它只是通过订阅进行循环。我们在这里只添加了一个订阅。另一方面,计时器必须非常频繁地检查,以确保在URL更改后立即触发事件。

此外,这比计时器更可靠,因为它消除了计时问题。

我希望能够添加locationchange事件监听器。经过下面的修改,我们就可以这样做了

window.addEventListener('locationchange', function () {
    console.log('location changed!');
});

相比之下,window.addEventListener('hashchange',() =>{})只会在url中标签后的部分发生变化时触发,而window.addEventListener('popstate',() =>{})并不总是工作。

这种修改类似于Christian的回答,修改了history对象以添加一些功能。

默认情况下,在这些修改之前,有一个popstate事件,但是没有pushstate和replacestate事件。

这将修改这三个函数,以便所有函数都触发一个自定义locationchange事件供您使用,如果您想使用这些事件,还可以推送state和replacestate事件。

这些是修改:

(() => {
    let oldPushState = history.pushState;
    history.pushState = function pushState() {
        let ret = oldPushState.apply(this, arguments);
        window.dispatchEvent(new Event('pushstate'));
        window.dispatchEvent(new Event('locationchange'));
        return ret;
    };

    let oldReplaceState = history.replaceState;
    history.replaceState = function replaceState() {
        let ret = oldReplaceState.apply(this, arguments);
        window.dispatchEvent(new Event('replacestate'));
        window.dispatchEvent(new Event('locationchange'));
        return ret;
    };

    window.addEventListener('popstate', () => {
        window.dispatchEvent(new Event('locationchange'));
    });
})();

注意,我们正在创建一个闭包,将旧函数保存为新函数的一部分,以便在调用新函数时调用它。

享受吧!

var previousUrl = '';
var observer = new MutationObserver(function(mutations) {
  if (location.href !== previousUrl) {
      previousUrl = location.href;
      console.log(`URL changed to ${location.href}`);
    }
});

看看jQuery的卸载函数。它能处理所有的事情。

https://api.jquery.com/unload/

当用户导航离开页面时,卸载事件被发送到窗口元素。这可能意味着许多事情之一。用户可以点击一个链接离开页面,或者在地址栏中输入一个新的URL。前进和后退按钮将触发事件。关闭浏览器窗口将触发该事件。即使是重新加载页面也会首先创建卸载事件。

$(window).unload(
    function(event) {
        alert("navigating");
    }
);