当 window.location.href 更改时发生

我正在为一个网站写一个 Greasemonkey 脚本,它在某些时候修改 location.href

window.location.href在页面上发生变化时,我怎样才能得到一个事件(通过 window.addEventListener或类似的东西) ?我还需要访问指向新的/修改后的 url 的文档的 DOM。

我见过其他涉及超时和轮询的解决方案,但如果可能的话,我希望避免这种情况。

279038 次浏览

你不能避免民意调查,没有任何事件改变 href。

如果你不太过火的话,使用间隔是相当轻的。如果你担心的话,每50毫秒左右检查一次 href 对性能没有任何显著影响。

你在卸载之前试过吗? 此事件在页面响应导航请求之前立即触发,这应该包括 href 的修改。

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<HTML>
<HEAD>
<TITLE></TITLE>
<META NAME="Generator" CONTENT="TextPad 4.6">
<META NAME="Author" CONTENT="?">
<META NAME="Keywords" CONTENT="?">
<META NAME="Description" CONTENT="?">
</HEAD>


<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function(){
$(window).unload(
function(event) {
alert("navigating");
}
);
$("#theButton").click(
function(event){
alert("Starting navigation");
window.location.href = "http://www.bbc.co.uk";
}
);


});
</script>




<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#FF0000" VLINK="#800000" ALINK="#FF00FF" BACKGROUND="?">


<button id="theButton">Click to navigate</button>


<a href="http://www.google.co.uk"> Google</a>
</BODY>
</HTML>

但是,请注意,您的事件将触发您从页面导航离开的 无论何时,无论这是因为脚本,还是有人点击链接。 你真正的挑战,是检测事件发生的不同原因。(如果这对你的逻辑很重要)

Popstate 事件 :

当活动历史条目更改时,将触发 popstate 事件。[ ... ] popstate 事件只有通过一个浏览器操作才能触发,比如点击后退按钮(或者在 JavaScript 中调用 history. back ())

因此,当使用 history.pushState()时,收听 popstate事件并发送 popstate事件应该足以对 href变化采取行动:

window.addEventListener('popstate', listener);


const pushUrl = (href) => {
history.pushState({}, '', href);
window.dispatchEvent(new Event('popstate'));
};

有一个默认的 onhashchange事件可以使用。

记录在案

可以这样使用:

function locationHashChanged( e ) {
console.log( location.hash );
console.log( e.oldURL, e.newURL );
if ( location.hash === "#pageX" ) {
pageX();
}
}


window.onhashchange = locationHashChanged;

如果浏览器不支持 oldURLnewURL,你可以这样绑定它:

//let this snippet run before your hashChange event binding code
if( !window.HashChangeEvent )( function() {
let lastURL = document.URL;
window.addEventListener( "hashchange", function( event ) {
Object.defineProperty( event, "oldURL", { enumerable: true, configurable: true, value: lastURL } );
Object.defineProperty( event, "newURL", { enumerable: true, configurable: true, value: document.URL } );
lastURL = document.URL;
} );
} () );

我在我的扩展“抓取任何媒体”中使用这个脚本并且工作得很好(就像 Youtube 上的案子)

var oldHref = document.location.href;


window.onload = function() {
var bodyList = document.querySelector("body")


var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (oldHref != document.location.href) {
oldHref = document.location.href;
/* Changed ! your code here */
}
});
});
    

var config = {
childList: true,
subtree: true
};
    

observer.observe(bodyList, config);
};


通过 Jquery试试

$(window).on('beforeunload', function () {
//your code goes here on location change
});

使用 Javascript:

window.addEventListener("beforeunload", function (event) {
//your code goes here on location change
});

参考文件: https://developer.mozilla.org/en-US/docs/Web/Events/beforeunload

改变 location.href有两种方法。您可以编写重新加载页面的 location.href = "y.html",也可以使用不重新加载页面的历史 API。我最近尝试了很多次。

如果打开一个子窗口并从父窗口捕获子页面的负载,那么不同的浏览器的行为非常不同。唯一常见的情况是,它们删除旧文档并添加一个新文档,因此,例如,向旧文档添加 readystatechange 或 load 事件处理程序不会产生任何效果。大多数浏览器也从窗口对象中删除事件处理程序,唯一的例外是 Firefox。在 Chrome 和 Karma 运行器中,如果你使用 unload + next tick,你可以在加载 readyState 中捕获新文档。因此,您可以添加例如加载事件处理程序或 readystatechange 事件处理程序,或者仅仅记录浏览器正在使用新的 URI 加载页面。在 Chrome 中使用手动测试(可能也是 GreaseMonkey) ,在 Opera、 PhantomJS、 IE10、 IE11中,在加载状态下无法捕获新文档。在这些浏览器中,unload + next tick调用回调比页面的加载事件触发晚几百毫秒。这种延迟通常是100到300毫秒,但 Opera simetime 会为下一个滴答声延迟750毫秒,这很可怕。因此,如果您想在所有浏览器中获得一致的结果,那么您可以在加载事件之后做您想做的事情,但是不能保证在此之前不会覆盖该位置。

var uuid = "win." + Math.random();
var timeOrigin = new Date();
var win = window.open("about:blank", uuid, "menubar=yes,location=yes,resizable=yes,scrollbars=yes,status=yes");




var callBacks = [];
var uglyHax = function (){
var done = function (){
uglyHax();
callBacks.forEach(function (cb){
cb();
});
};
win.addEventListener("unload", function unloadListener(){
win.removeEventListener("unload", unloadListener); // Firefox remembers, other browsers don't
setTimeout(function (){
// IE10, IE11, Opera, PhantomJS, Chrome has a complete new document at this point
// Chrome on Karma, Firefox has a loading new document at this point
win.document.readyState; // IE10 and IE11 sometimes fails if I don't access it twice, idk. how or why
if (win.document.readyState === "complete")
done();
else
win.addEventListener("load", function (){
setTimeout(done, 0);
});
}, 0);
});
};
uglyHax();




callBacks.push(function (){
console.log("cb", win.location.href, win.document.readyState);
if (win.location.href !== "http://localhost:4444/y.html")
win.location.href = "http://localhost:4444/y.html";
else
console.log("done");
});
win.location.href = "http://localhost:4444/x.html";

如果你只在 Firefox 中运行你的脚本,那么你可以使用一个简化版本并在加载状态下捕获文档,例如,在你记录 URI 更改之前,加载页面上的脚本不能导航离开:

var uuid = "win." + Math.random();
var timeOrigin = new Date();
var win = window.open("about:blank", uuid, "menubar=yes,location=yes,resizable=yes,scrollbars=yes,status=yes");




var callBacks = [];
win.addEventListener("unload", function unloadListener(){
setTimeout(function (){
callBacks.forEach(function (cb){
cb();
});
}, 0);
});




callBacks.push(function (){
console.log("cb", win.location.href, win.document.readyState);
// be aware that the page is in loading readyState,
// so if you rewrite the location here, the actual page will be never loaded, just the new one
if (win.location.href !== "http://localhost:4444/y.html")
win.location.href = "http://localhost:4444/y.html";
else
console.log("done");
});
win.location.href = "http://localhost:4444/x.html";

如果我们讨论的是单页应用程序,它改变了 URI 的 hash 部分,或者使用了历史 API,那么您可以分别使用窗口的 hashchangepopstate事件。即使你在历史中来回移动,直到你停留在同一页上,这些都可以捕捉到。文档不会因为这些变化而改变,页面也不会真正重新加载。

试试这个脚本,它可以让你在 URL 发生变化时运行代码(不需要页面加载,比如单页应用程序) :

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

根据“ Leonardo Ciaccio”的回答,修改后的代码如下: 例如,删除循环并重新分配 Body Element,如果它被删除的话

window.addEventListener("load", function() {
let oldHref = document.location.href,
bodyDOM = document.querySelector("body");
const observer = new MutationObserver(function(mutations) {
if (oldHref != document.location.href) {
oldHref = document.location.href;
console.log("the location href is changed!");
window.requestAnimationFrame(function() {
let tmp = document.querySelector("body");
if(tmp != bodyDOM) {
bodyDOM = tmp;
observer.observe(bodyDOM, config);
}
})
}
});
const config = {
childList: true,
subtree: true
};
observer.observe(bodyDOM, config);
}, true);