是否有一个简单的钩子来检测脚本打开的窗口是否已完成加载?基本上,我想要相当于onLoad()
钩子,但是我不能直接设置它 - 假设子文档是给定的,我实际上不能在其中放入我自己的任何代码。
例如,假设我有以下两个文件:
parent.html
:
<html>
<head>
<title>Parent</title>
</head>
<script type="text/javascript">
var w;
function loadChild() {
w = window.open();
w.location.href="child.html";
// block until child has finished loading... how?
w.doSomething();
}
</script>
</html>
<body>
I am a parent window. <a href="javascript:loadChild()">Click me</a>.
</body>
child.html
:
<html>
<head>
<title>Child</title>
</head>
<script type="text/javascript">
function doSomething() {
alert("Hi there");
}
</script>
</html>
<body>
I am a child window
</body>
由于设置location.href是非阻止的,因此尚未定义w.doSomething()
并且doSomething()
调用会爆炸。如何检测孩子已完成装载?
答案 0 :(得分:20)
如果新打开的窗口的位置是同源的,则此方法有效:
var w = window.open('child.html')
w.addEventListener('load', w.doSomething, true);
答案 1 :(得分:5)
怎么样
parent.html:
<html>
<head>
<title>Parent</title>
</head>
<script type="text/javascript">
var w;
function loadChild() {
w = window.open();
w.location.href="child.html";
// like this (with jquery)
$(w).ready(function()
{
w.doSomething();
});
}
</script>
</html>
<body>
I am a parent window. <a href="javascript:loadChild()">Click me</a>.
</body>
答案 2 :(得分:3)
接受的答案不能解决原始问题:
w = window.open();
w.location.href="child.html";
// block until child has finished loading... how?
w.doSomething();
为了解决这个问题,我们需要更多地了解页面加载在后台如何进行。实际上,这是异步的,因此当您编写w.location.href="child.html";
并在其后立即调用w.doSomething();
时,它不会等到新页面加载完毕。尽管浏览器开发人员很好地解决了iframe的加载问题,但对于子窗口却并非如此。没有相应的API,因此您所能做的就是编写某种解决方法。您可以做的是使用子窗口的unload事件侦听器中的适当defer函数:
w.addEventListener("unload", function (){
defer(function (){
w.doSomething();
});
});
与客户端js开发一样,每种浏览器的工作方式完全不同。
最好的解决方案是使用defer函数,该函数在子窗口的文档处于loading readyState时调用回调,您可以在其中添加加载处理程序。如果在此之前调用了回调,那么在当前浏览器中,尚未创建新文档,因此将加载处理程序添加到旧文档中,之后将其替换为新文档,因此加载处理程序将迷路了。唯一的例外是Firefox,因为该浏览器会将负载处理程序添加到窗口中。如果浏览器在加载readyState之后调用defer回调,则在某些当前浏览器中,实际加载页面后可能会有超过2500毫秒的延迟。我不知道为什么某些浏览器由于子窗口文档加载而延迟了某些时间。我搜索了一段时间,但没有找到任何答案。有些浏览器没有任何“加载”延迟,因此,您所能做的就是使用“延迟”延迟,并希望达到最佳效果。根据我的测试结果,基于MessageChannel的解决方案是最好的多浏览器“加载”延迟:
function defer (callback) {
var channel = new MessageChannel();
channel.port1.onmessage = function (e) {
callback();
};
channel.port2.postMessage(null);
}
因此您可以执行以下操作:
w.addEventListener("unload", function (){
// note: Safari supports pagehide only
defer(function (){
if (w.document.readyState === "loading")
w.addEventListener("load", function (){
w.doSomething();
});
else
w.doSomething();
});
});
如果要支持Safari,则应使用pagehide而不是卸载。 pagehide event is supported from IE 11,因此,如果您想支持甚至更老的浏览器,则必须同时使用卸载和页面隐藏,并且仅在其中一个可用时才使用其中的一个启动延迟。
var awaitLoad = function (win, cb){
var wasCalled = false;
function unloadListener(){
if (wasCalled)
return;
wasCalled = true;
win.removeEventListener("unload", unloadListener);
win.removeEventListener("pagehide", unloadListener);
// Firefox keeps window event listeners for multiple page loads
defer(function (){
win.document.readyState;
// IE sometimes throws security error if not accessed 2 times
if (win.document.readyState === "loading")
win.addEventListener("load", function loadListener(){
win.removeEventListener("load", loadListener);
cb();
});
else
cb();
});
};
win.addEventListener("unload", unloadListener);
win.addEventListener("pagehide", unloadListener);
// Safari does not support unload
});
w = window.open();
w.location.href="child.html";
awaitLoad(w, function (){
w.doSomething();
});
如果支持Promises和async功能,则可以使用以下内容:
w = window.open();
await load(w, "child.html");
w.doSomething();
但这是一个不同的故事...