Answers:
在现代浏览器(IE8 +,FF3.6 +,Chrome)中,您只能在上收听hashchange
事件window
。
在某些旧的浏览器中,您需要一个计时器来不断检查location.hash
。如果您使用的是jQuery,则有一个插件可以完全做到这一点。
beforeunload
事件。如果您的代码启动了URL更改,那将是最好的。
我希望能够添加locationchange
事件侦听器。经过下面的修改,我们就可以做到,像这样
window.addEventListener('locationchange', function(){
console.log('location changed!');
})
相反,window.addEventListener('hashchange',()=>{})
只有在url中的#标签后面的部分发生更改并且window.addEventListener('popstate',()=>{})
并不总是起作用时,才会触发。
此修改类似于Christian的答案,修改了历史对象以添加一些功能。
默认情况下,有一个popstate
事件,但也有不活动pushstate
,和replacestate
。
这会修改这三个功能,使所有的消防自定义locationchange
事件供您使用,也pushstate
和replacestate
活动,如果你想使用这些:
/* 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'))
});
使用此代码
window.onhashchange = function() {
//code
}
与jQuery
$(window).bind('hashchange', function() {
//code
});
经过一些研究后进行编辑:
似乎我被Mozilla文档中的文档所迷住了。每当在代码中调用或时,都不会触发该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));
};
目前(2017年1月),全球92%的浏览器都支持popstate。
使用jquery(和插件),您可以执行
$(window).bind('hashchange', function() {
/* things */
});
http://benalman.com/projects/jquery-hashchange-plugin/
否则,您将必须使用setInterval并检查hash事件(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();
window.addEventListener("hashchange", hashChanged);
detect()
功能时太忙?
添加哈希更改事件侦听器!
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
}
这个解决方案为我工作:
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();
setInterval
为setTimeout
:“使用setInterval()会在一段时间后使浏览器停止,因为它将每秒创建一个对checkURLchange()的新调用。setTimeout()是正确的隔离,因为它只能被调用一次。”
setTimeout
@divibisan建议的方法,而是setInterval
将函数移到外部。checkURLchange();
也变为可选。
尽管是一个老问题,但位置栏项目非常有用。
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});
要收听网址更改,请参见以下内容:
window.onpopstate = function(event) {
console.log("location: " + document.location + ", state: " + JSON.stringify(event.state));
};
如果您打算在某些特定条件后停止/删除侦听器,请使用此样式。
window.addEventListener('popstate', function(e) {
console.log('url changed')
});
在进行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,因此不是火箭科学,而是我发现的最干净的解决方案。
window.history
的最大长度为50(至少从Chrome 80开始)。此后,window.history.length
始终返回50。发生这种情况时,此方法将无法识别任何更改。
查看jQuery卸载功能。它处理所有事情。
https://api.jquery.com/unload/
当用户离开页面导航时,将把unload事件发送到window元素。这可能意味着很多事情之一。用户可能已经单击了链接以离开页面,或者在地址栏中输入了新的URL。前进和后退按钮将触发事件。关闭浏览器窗口将导致事件被触发。即使页面重新加载也会首先创建一个卸载事件。
$(window).unload(
function(event) {
alert("navigating");
}
);
下面的答案来自这里(使用旧的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"));
});
})();
您setInterval
在每次通话时都开始一个新通话,而不会取消前一个通话-可能您只是想拥有一个setTimeout