211

如何检查 JavaScript 中的 URL 是否已更改?例如,使用 AJAX 的 GitHub 等网站将在 # 符号后附加页面信息以创建唯一的 URL,而无需重新加载页面。检测此 URL 是否更改的最佳方法是什么?

  • onload是否再次调用该事件?
  • URL 是否有事件处理程序?
  • 还是必须每秒检查一次 URL 以检测更改?
4

21 回答 21

181

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

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

相反,window.addEventListener('hashchange',()=>{})仅当 url 中的主题标签之后的部分发生更改时才会触发,并且window.addEventListener('popstate',()=>{})并不总是有效。

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

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

这会修改这三个函数,以便所有函数都会触发一个自定义locationchange事件供您使用,如果您想使用它们,pushstate还可以触发事件。replacestate

这些是修改:

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

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

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

笔记:

我们正在创建一个闭包old = (f=>function new(){f();...})(old)替换为包含之前保存在old其中的函数(此时未运行,但将在其中运行)newoldoldnew

于 2018-10-15T02:47:34.387 回答
129

在现代浏览器(IE8+、FF3.6+、Chrome)中,您只需hashchangewindow.

在一些旧浏览器中,您需要一个不断检查location.hash. 如果您使用的是 jQuery,那么有一个插件可以做到这一点。

例子

下面我撤消任何 URL 更改,以保持滚动:

<script type="text/javascript">
  if (window.history) {
    var myOldUrl = window.location.href;
    window.addEventListener('hashchange', function(){
      window.history.pushState({}, null, myOldUrl);
    });
  }
</script>

请注意,上面使用history的 -API 在 Chrome、Safari、Firefox 4+ 和 Internet Explorer 10pp4+ 中可用

于 2011-06-17T18:50:19.050 回答
94
window.onhashchange = function() { 
     //code  
}

window.onpopstate = function() { 
     //code  
}

或者

window.addEventListener('hashchange', function() { 
  //code  
});

window.addEventListener('popstate', function() { 
  //code  
});

用 jQuery

$(window).bind('hashchange', function() {
     //code
});

$(window).bind('popstate', function() {
     //code
});
于 2016-05-07T17:18:36.000 回答
72

经过一番研究后编辑:

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

但是,有一种方法可以通过根据 @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 。

于 2017-01-24T09:56:54.077 回答
52

使用 jquery(和插件),您可以做到

$(window).bind('hashchange', function() {
 /* things */
});

http://benalman.com/projects/jquery-hashchange-plugin/

否则是的,您将不得不使用 setInterval 并检查散列事件(window.location.hash)中的变化

更新!一个简单的草稿

function hashHandler(){
    this.oldHash = window.location.hash;
    this.Check;

    var that = this;
    var detect = function(){
        if(that.oldHash!=window.location.hash){
            alert("HASH CHANGED - new has" + window.location.hash);
            that.oldHash = window.location.hash;
        }
    };
    this.Check = setInterval(function(){ detect() }, 100);
}

var hashDetection = new hashHandler();
于 2011-06-17T18:49:20.143 回答
27

添加哈希更改事件侦听器!

window.addEventListener('hashchange', function(e){console.log('hash changed')});

或者,要监听所有 URL 更改:

window.addEventListener('popstate', function(e){console.log('url changed')});

这比下面的代码要好,因为 window.onhashchange 中只能存在一件事,并且您可能会覆盖其他人的代码。

// Bad code example

window.onhashchange = function() { 
     // Code that overwrites whatever was previously in window.onhashchange  
}
于 2017-06-01T23:26:27.073 回答
9

这个解决方案对我有用:

function checkURLchange(){
    if(window.location.href != oldURL){
        alert("url changed!");
        oldURL = window.location.href;
    }
}

var oldURL = window.location.href;
setInterval(checkURLchange, 1000);
于 2016-04-05T23:37:20.917 回答
4

虽然是一个老问题,但Location-bar项目非常有用。

var LocationBar = require("location-bar");
var locationBar = new LocationBar();

// listen to all changes to the location bar
locationBar.onChange(function (path) {
  console.log("the current url is", path);
});

// listen to a specific change to location bar
// e.g. Backbone builds on top of this method to implement
// it's simple parametrized Backbone.Router
locationBar.route(/some\-regex/, function () {
  // only called when the current url matches the regex
});

locationBar.start({
  pushState: true
});

// update the address bar and add a new entry in browsers history
locationBar.update("/some/url?param=123");

// update the address bar but don't add the entry in history
locationBar.update("/some/url", {replace: true});

// update the address bar and call the `change` callback
locationBar.update("/some/url", {trigger: true});
于 2015-05-20T14:14:20.893 回答
4

要收听 url 更改,请参见下文:

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

如果您打算在某些特定条件后停止/删除侦听器,请使用此样式。

window.addEventListener('popstate', function(e) {
   console.log('url changed')
});
于 2017-12-20T09:01:47.293 回答
3

当单击链接将您重定向到同一域上的不同页面时,这些似乎都不起作用。因此,我提出了自己的解决方案:

let pathname = location.pathname;
window.addEventListener("click", function() {
    if (location.pathname != pathname) {
        pathname = location.pathname;
        // code
    }
});

编辑:您还可以检查 popstate 事件(如果用户返回页面)

window.addEventListener("popstate", function() {
    // code
});

最好的祝愿,

结石

于 2021-07-14T03:32:40.027 回答
2

The answer below comes from here(with old javascript syntax(no arrow function, support IE 10+)): https://stackoverflow.com/a/52809105/9168962

(function() {
  if (typeof window.CustomEvent === "function") return false; // If not IE
  function CustomEvent(event, params) {
    params = params || {bubbles: false, cancelable: false, detail: null};
    var evt = document.createEvent("CustomEvent");
    evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
    return evt;
  }
  window.CustomEvent = CustomEvent;
})();

(function() {
  history.pushState = function (f) {
    return function pushState() {
      var ret = f.apply(this, arguments);
      window.dispatchEvent(new CustomEvent("pushState"));
      window.dispatchEvent(new CustomEvent("locationchange"));
      return ret;
    };
  }(history.pushState);
  history.replaceState = function (f) {
    return function replaceState() {
      var ret = f.apply(this, arguments);
      window.dispatchEvent(new CustomEvent("replaceState"));
      window.dispatchEvent(new CustomEvent("locationchange"));
      return ret;
    };
  }(history.replaceState);
  window.addEventListener("popstate", function() {
    window.dispatchEvent(new CustomEvent("locationchange"));
  });
})();
于 2019-08-21T08:06:11.977 回答
2

在做一点 chrome 扩展时,我遇到了同样的问题,还有一个问题:有时,页面会改变,但 URL 不会改变。

例如,只需转到 Facebook 主页,然后单击“主页”按钮。您将重新加载页面,但 URL 不会更改(单页应用程序样式)。

99% 的时间,我们都在开发网站,所以我们可以从 Angular、React、Vue 等框架中获取这些事件。

但是,在我的 Chrome 扩展程序(在 Vanilla JS 中)的情况下,我必须监听一个将触发每个“页面更改”的事件,这通常可以通过 URL 更改来捕获,但有时它不会。

我自制的解决方案如下:

listen(window.history.length);
var oldLength = -1;
function listen(currentLength) {
  if (currentLength != oldLength) {
    // Do your stuff here
  }

  oldLength = window.history.length;
  setTimeout(function () {
    listen(window.history.length);
  }, 1000);
}

所以基本上 leoneckert 解决方案,应用于窗口历史,当页面在单页应用程序中发生变化时,它会改变。

不是火箭科学,而是我找到的最干净的解决方案,考虑到我们在这里只检查整数相等,而不是更大的对象或整个 DOM。

于 2018-04-11T10:27:25.323 回答
2

如果没有任何窗口事件对您有用(因为在我的情况下它们不是),您还可以使用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 发生变化,根元素的内容也会发生变化。

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

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

于 2021-03-20T15:35:13.610 回答
1

在单独的线程中找到了一个有效的答案:

没有一个事件总是有效的,猴子修补 pushState 事件对于大多数主要的 SPA 来说都是非常成功或错过的。

所以智能投票对我来说是最有效的。您可以添加任意数量的事件类型,但这些似乎对我来说做得很好。

为 TS 编写,但易于修改:

const locationChangeEventType = "MY_APP-location-change";

// called on creation and every url change
export function observeUrlChanges(cb: (loc: Location) => any) {
  assertLocationChangeObserver();
  window.addEventListener(locationChangeEventType, () => cb(window.location));
  cb(window.location);
}

function assertLocationChangeObserver() {
  const state = window as any as { MY_APP_locationWatchSetup: any };
  if (state.MY_APP_locationWatchSetup) { return; }
  state.MY_APP_locationWatchSetup = true;

  let lastHref = location.href;

  ["popstate", "click", "keydown", "keyup", "touchstart", "touchend"].forEach((eventType) => {
    window.addEventListener(eventType, () => {
      requestAnimationFrame(() => {
        const currentHref = location.href;
        if (currentHref !== lastHref) {
          lastHref = currentHref;
          window.dispatchEvent(new Event(locationChangeEventType));
        }
      })
    })
  });
}

用法

observeUrlChanges((loc) => {
  console.log(loc.href)
})
于 2020-10-06T19:20:02.417 回答
0

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

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

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

$(window).unload(
    function(event) {
        alert("navigating");
    }
);
于 2016-01-06T16:30:00.530 回答
0

我创建了这个,只需将函数添加为参数,只要链接有任何更改,它将运行返回旧 URL 和新 URL 的函数

我创建了这个,只需将函数添加为参数,只要链接有任何更改,它将运行返回旧 URL 和新 URL 的函数

// on-url-change.js v1 (manual verification)
let onUrlChangeCallbacks = [];
let onUrlChangeTimestamp = new Date() * 1;
function onUrlChange(callback){
    onUrlChangeCallbacks.push(callback);
};
onUrlChangeAutorun();
function onUrlChangeAutorun(){
    let oldURL = window.location.href;
    setInterval(function(){
        let newURL = window.location.href;
        if(oldURL !== newURL){
            let event = {
                oldURL: oldURL,
                newURL: newURL,
                type: 'urlchange',
                timestamp: new Date() * 1 - onUrlChangeTimestamp
            };
            oldURL = newURL;
            for(let i = 0; i < onUrlChangeCallbacks.length; i++){
                onUrlChangeCallbacks[i](event);
            };
        };
    }, 25);
};
于 2020-11-03T14:33:20.490 回答
0

享受!

var previousUrl = '';
var observer = new MutationObserver(function(mutations) {
  if (location.href !== previousUrl) {
      previousUrl = location.href;
      console.log(`URL changed to ${location.href}`);
    }
});
于 2021-06-03T14:04:21.737 回答
0

setInterval在每次通话时都会开始新的通话,而不会取消之前的通话 - 可能您只是想拥有一个setTimeout

于 2017-01-26T13:00:18.697 回答
0
window.addEventListener("beforeunload", function (e) {
    // do something
}, false);
于 2016-05-01T12:14:09.943 回答
-1

我来这里是因为标题,但听hashchange事件(在 上可用window)不是解决方案。

首先,确保以下执行一次:

history.pushState = (function (oldPushFunc) {
    return function () {
        var ret = oldPushFunc.apply(this, arguments);
        window.dispatchEvent(new Event('pushstate'));
        window.dispatchEvent(new Event('myUrlChangeEvent'));
        return ret;
    };
})(history.pushState);

history.replaceState = (function (oldReplaceFunc) {
    return function () {
        var ret = oldReplaceFunc.apply(this, arguments);
        window.dispatchEvent(new Event('replacestate'));
        window.dispatchEvent(new Event('myUrlChangeEvent'));
        return ret;
    };
})(history.replaceState);

window.addEventListener('popstate', function () {
    window.dispatchEvent(new Event('myUrlChangeEvent'));
});

上面将 a 包裹function在 anotherfunction中,只是为了创建一个 Clousor,它确实通过oldPushFunc并且oldReplaceFunc不会向全局变量发送垃圾邮件。

最后,使用我们创建的自定义事件

window.addEventListener('myUrlChangeEvent', function () {
    console.log('URL did change!');
});

请注意,上面使用history的 -API 在所有现代浏览器中都可用,例如 Chrome、Safari、Firefox 4+ 和 Internet Explorer 10pp4+)。

于 2022-01-27T17:14:13.157 回答
-1

另一个简单的方法是添加一个点击事件,通过一个类名到页面上的锚标签来检测它何时被点击,然后你现在可以使用 window.location.href 来获取 url 数据您可以使用向服务器运行 ajax 请求。简单易行。

于 2020-04-02T14:04:54.957 回答