如果打开一个子窗口并从父窗口捕获子页面的负载,那么不同的浏览器的行为非常不同。唯一常见的情况是,它们删除旧文档并添加一个新文档,因此,例如,向旧文档添加 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,那么您可以分别使用窗口的 hashchange和 popstate事件。即使你在历史中来回移动,直到你停留在同一页上,这些都可以捕捉到。文档不会因为这些变化而改变,页面也不会真正重新加载。
var previousUrl = '';
var observer = new MutationObserver(function(mutations) {
if (location.href !== previousUrl) {
previousUrl = location.href;
console.log(`URL changed to ${location.href}`);
}
});