如何检查URL是否在JavaScript中发生了变化?例如,像GitHub这样使用AJAX的网站,会在#符号后附加页面信息,以创建唯一的URL,而无需重新加载页面。检测该URL是否发生变化的最佳方法是什么?
是否再次调用onload事件? 是否有URL的事件处理程序? 或者必须每秒钟检查URL以检测更改?
如何检查URL是否在JavaScript中发生了变化?例如,像GitHub这样使用AJAX的网站,会在#符号后附加页面信息,以创建唯一的URL,而无需重新加载页面。检测该URL是否发生变化的最佳方法是什么?
是否再次调用onload事件? 是否有URL的事件处理程序? 或者必须每秒钟检查URL以检测更改?
当前回答
您在每次调用时都开始一个新的setInterval,而没有取消前一个——可能您只是想要一个setTimeout
其他回答
在一个单独的线程中找到了一个工作答案:
没有一个事件是永远有效的,对于大多数大型spa来说,monkey patch 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)
})
如果没有窗口事件为您工作(在我的例子中不是这样),您还可以使用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更改后立即触发事件。
此外,这比计时器更可靠,因为它消除了计时问题。
我创建了这个与hashchange事件非常相似的事件
// onurlchange-event.js v1.0.1
(() => {
const hasNativeEvent = Object.keys(window).includes('onurlchange')
if (!hasNativeEvent) {
let oldURL = location.href
setInterval(() => {
const newURL = location.href
if (oldURL === newURL) {
return
}
const urlChangeEvent = new CustomEvent('urlchange', {
detail: {
oldURL,
newURL
}
})
oldURL = newURL
dispatchEvent(urlChangeEvent)
}, 25)
addEventListener('urlchange', event => {
if (typeof(onurlchange) === 'function') {
onurlchange(event)
}
})
}
})()
使用示例:
window.onurlchange = event => {
console.log(event)
console.log(event.detail.oldURL)
console.log(event.detail.newURL)
}
addEventListener('urlchange', event => {
console.log(event)
console.log(event.detail.oldURL)
console.log(event.detail.newURL)
})
这个解决方案对我很有效:
function checkURLchange(){
if(window.location.href != oldURL){
alert("url changed!");
oldURL = window.location.href;
}
}
var oldURL = window.location.href;
setInterval(checkURLchange, 1000);
我希望能够添加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'));
});
})();
注意,我们正在创建一个闭包,将旧函数保存为新函数的一部分,以便在调用新函数时调用它。