如何检测JavaScript中的URL更改

AJ0*_*200 132 javascript ajax

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

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

phi*_*hag 93

在现代浏览器(IE8 +,FF3.6 +,Chrome)中,您可以直接收听该hashchange事件window.

在某些旧版浏览器中,您需要一个不断检查的计时器location.hash.如果您正在使用jQuery,那么有一个插件就可以完成.

  • 据我所知,这只适用于#符号之后的部分更改(因此事件名称)?而不是完整的URL更改,正如问题的标题所暗示的那样. (116认同)
  • @NPC任何完整URL更改的处理程序(没有锚标记)? (10认同)

Beh*_*adi 68

使用此代码

window.onhashchange = function() { 
     //code  
}
Run Code Online (Sandbox Code Playgroud)

用jQuery

$(window).bind('hashchange', function() {
     //code
});
Run Code Online (Sandbox Code Playgroud)

  • 如果仅在网址中存在哈希值时触发此操作,那么这是最佳答案? (17认同)
  • 不适用于非哈希网址更改,这些更改似乎很受欢迎,例如Slack实现的更改 (10认同)
  • 这必须标明答案.它是一行,使用浏览器事件模型,不依赖于无限的资源消耗超时 (7认同)
  • 在我看来,这是这里最好的答案。没有插件和最少的代码 (2认同)
  • 您应该使用 addEventListener 而不是直接替换 onhashchange 值,以防其他东西也想监听。 (2认同)

Pan*_*lis 55

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

$(window).bind('hashchange', function() {
 /* things */
});
Run Code Online (Sandbox Code Playgroud)

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();
Run Code Online (Sandbox Code Playgroud)

  • 你可以@BergP,使用普通的javascript监听器:`window.addEventListener("hashchange",hashChanged);` (6认同)
  • @HasibMahmud,该代码每100ms进行1次相等检查.我只是在我的浏览器中进行基准测试,我可以在1ms内完成500次相等检查.因此该代码使用了我的处理能力的1/50000.我不会太担心. (4认同)

alj*_*gom 52

我想要的是能够添加locationchange事件侦听器。有了这个修改,我们可以做这样的事情

window.addEventListener('locationchange', function(){
    console.log('location changed!');
})
Run Code Online (Sandbox Code Playgroud)

相反,window.addEventListener('hashchange',()=>{})只有在url中的#标签后面的部分发生更改并且window.addEventListener('popstate',()=>{})并不总是起作用时,才会触发。

此修改类似于Christian的答案,修改了历史对象以添加一些功能。

默认情况下,有一个popstate事件,但没有针对pushstate和的事件replacestate

这会修改这三个功能,使所有的消防自定义locationchange事件供您使用,也pushstatereplacestate活动,如果你想使用这些:

/* These are the modifications: */
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'))
});
Run Code Online (Sandbox Code Playgroud)

  • 为什么这不是公认的答案 (11认同)
  • 这非常有帮助,并且很有魅力。这应该是公认的答案。 (3认同)
  • 太好了,我需要什么谢谢 (2认同)
  • @joshuacotton => 是一个箭头函数,您可以将 f => function fname(){...} 替换为 function(f){ return function fname(){...} } (2认同)

Cri*_*ian 47

经过一番研究后编辑:

在某种程度上,我似乎被Mozilla文档中的文档所迷惑.该popstate事件(和它的回调函数onpopstate)都不会触发每当pushState()replaceState()称为代码.因此,原始答案并不适用于所有情况.

但是有一种方法可以通过根据@ alpha123修补函数来避免这种情况:

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

原始答案

鉴于此问题的标题是" 如何检测URL更改 "的答案,当您想知道完整路径何时更改(而不仅仅是散列锚)时,您可以监听popstate事件:

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

Mozilla文档中的popstate参考

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

  • 令人难以置信的是,我们仍然必须在2018年诉诸此类黑客攻击。 (5认同)
  • 如果不使用 location.back(),这不会检测到从 localhost/foo 到 localhost/baa 的更改 (3认同)
  • 令人难以置信的是,我们必须在 2022 年诉诸此类黑客手段。 (3认同)
  • 这适用于我的用例 - 但就像 @goat 所说 - 令人难以置信的是没有对此的本机支持...... (2认同)
  • 什么论据?我将如何设置 fireEvents? (2认同)
  • 请注意,这在许多情况下也不可靠。例如,当您单击其他Amazon产品变体(价格下方的图块)时,它不会检测到URL更改。 (2认同)

wen*_*ner 22

适用于 Chrome 102+ (2022-05-24)

navigation.addEventListener("navigate", e => {
  console.log(`navigate ->`,e.destination.url)
});
Run Code Online (Sandbox Code Playgroud)

API 参考WICG/navigation-api


Tre*_*v14 20

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

window.addEventListener('hashchange', function(e){console.log('hash changed')});
Run Code Online (Sandbox Code Playgroud)

或者,要收听所有网址更改:

window.addEventListener('popstate', function(e){console.log('url changed')});
Run Code Online (Sandbox Code Playgroud)

这比下面的代码更好,因为window.onhashchange中只能存在一个东西,你可能会覆盖别人的代码.

// Bad code example

window.onhashchange = function() { 
     // Code that overwrites whatever was previously in window.onhashchange  
}
Run Code Online (Sandbox Code Playgroud)

  • 弹出状态仅在您弹出状态时触发,而不是推送状态 (2认同)
  • 这仅在使用浏览器的后退和前进按钮导航时有效,即在许多情况下完全无用。 (2认同)

Jar*_*ach 11

如果没有一个窗口事件对您有用(因为它们不适用于我的情况),您还可以使用 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
  });
Run Code Online (Sandbox Code Playgroud)

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

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

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


Cal*_*ise 9

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

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

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

window.addEventListener("popstate", function() {
    // code
});
Run Code Online (Sandbox Code Playgroud)

最好的祝愿,

结石


小智 7

这个解决方案对我有用:

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

    oldURL = window.location.href;
    setInterval(function() {
        checkURLchange(window.location.href);
    }, 1000);
}

checkURLchange();
Run Code Online (Sandbox Code Playgroud)

  • 这是一个相当简陋的方法,我认为我们可以更高的目标. (13认同)
  • 虽然我同意@CarlesAlcolea认为这感觉很旧,但根据我的经验,它仍然是捕获100%所有网址更改的唯一方法. (5认同)
  • @ahofmann建议(在应有评论的编辑中)将setInterval更改为setTimeout:“使用setInterval()会在一段时间后使浏览器停止,因为它将创建对checkURLchange()的新调用每秒。setTimeout()是正确的隔离,因为它仅被调用一次。 (3认同)
  • 或者不要像 @dibibisan 建议的那样使用 `setTimeout`,而是将 `setInterval` 移到函数之外。`checkURLchange();` 也变成可选的。 (3认同)
  • 我同意,从所有答案来看,这是我能够捕获所有 URL 更改的唯一方法。 (3认同)