等待子窗口加载完成

时间:2009-09-03 07:59:25

标签: javascript

是否有一个简单的钩子来检测脚本打开的窗口是否已完成加载?基本上,我想要相当于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()调用会爆炸。如何检测孩子已完成装载?

3 个答案:

答案 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开发一样,每种浏览器的工作方式完全不同。

enter image description here

最好的解决方案是使用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();

但这是一个不同的故事...