如何检测JavaScript中的URL更改

时间:2011-06-17 18:45:56

标签: javascript ajax

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

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

20 个答案:

答案 0 :(得分:85)

在现代浏览器(IE8 +,FF3.6 +,Chrome)中,您只需在hashchange上收听window事件。

在某些旧版浏览器中,您需要一个不断检查location.hash的计时器。如果您正在使用jQuery,那么plugin就是这样做的。

答案 1 :(得分:63)

使用此代码

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

使用jQuery

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

答案 2 :(得分:54)

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

答案 3 :(得分:36)

经过一番研究后编辑:

在某种程度上,我似乎被Mozilla文档中的文档所迷惑。每当在代码中调用popstateonpopstate时,pushState()事件(及其回调函数replaceState())都为not triggered。因此,原始答案并不适用于所有情况。

但是有一种方法可以通过monkey-patching the functions according to @alpha123来解决这个问题:

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

原始回答

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

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

Reference for popstate in Mozilla Docs

目前(2017年1月)全球有support for popstate from 92%个浏览器。

答案 4 :(得分:16)

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

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

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

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  
}

答案 5 :(得分:11)

我想要的是能够添加locationchange事件监听器,并且通过此修改,我们可以执行以下操作

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

相比之下,window.addEventListener('hashchange',()=>{})仅在网址中的#标签后面的部分发生更改并且window.addEventListener('popstate',()=>{})并不总是有效时才会触发。

Christian's answer类似,此修饰符修改了历史对象以添加一些功能。

默认情况下,有一个popstate事件,但没有pushstatereplacestate的事件

这会修改这三个函数,以便所有事件都触发一个自定义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'))
});

答案 6 :(得分:6)

这个解决方案对我有用:

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

答案 7 :(得分: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});

答案 8 :(得分:3)

要收听网址更改,请参阅以下内容:

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

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

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

答案 9 :(得分:1)

在进行一些Chrome扩展时,我遇到了另外一个问题:有时,页面会更改,但不会更改网址。

例如,只需转到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。

答案 10 :(得分:1)

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

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

  

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

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

答案 11 :(得分:1)

如果没有任何窗口事件适合您(因为它们在我的情况下不是),您还可以使用 swift documentation 来查看根元素(非递归)。

const myWeakRef = new WeakRef({
  name: 'Cache',
  size: 'unlimited'
})

// Log the value of "myWeakRef":
console.log(myWeakRef.deref())

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

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

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

答案 12 :(得分:0)

window.addEventListener("beforeunload", function (e) {
    // do something
}, false);

答案 13 :(得分:0)

separate thread中找到了可行的答案:

没有一个事件可以一直有效,对于大多数主要的SPA来说,修补pushState事件的猴子都是很受欢迎的。

因此,智能轮询对我来说是最有效的方法。您可以添加任意数量的事件类型,但是这些事件对我来说似乎做得很好。

为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)
})

答案 14 :(得分:0)

我创建了这个函数,只需将函数添加为参数,只要链接有任何更改,它将运行该函数并返回旧的和新的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);
};

答案 15 :(得分:0)

享受吧!

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

答案 16 :(得分:0)

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

let loc = location.pathname;
window.onclick = () => {
    if (location.pathname != loc) {
        loc = location.pathname;
        // code
    }
}

最好的祝福,

微积分

答案 17 :(得分:-1)

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

答案 18 :(得分:-1)

以下答案来自此处(使用旧的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"));
  });
})();

答案 19 :(得分:-1)

执行此操作的另一种简单方法是,通过在类的名称上添加click事件,将其添加到页面上的定位标记中以检测何时单击了该事件,然后您可以使用window.location.href获取可以用于向服务器运行ajax请求的url数据。简单又容易。