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

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


当前回答

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

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

or

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

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

与jQuery

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

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

其他回答

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

经过一番研究后编辑:

不知怎么的,我似乎被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更改后立即触发事件。

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

下面的答案来自这里(使用旧的javascript语法(没有箭头函数,支持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"));
  });
})();

在做一点chrome扩展时,我遇到了一个额外的问题:有时,页面变化而不是URL。

例如,只要进入Facebook主页,然后点击“主页”按钮。你将重新加载页面,但URL不会改变(单页应用程序样式)。

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

但是,在我的Chrome扩展(在香草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。