检测Iframe内容何时加载完成(跨浏览器)

102

我试图检测 iframe 及其内容何时加载完成,但是没有太大的成功。我的应用程序在父窗口中的文本字段中接受一些输入并更新 iframe 以提供“实时预览”。

我最初使用以下代码(YUI)来检测 iframe 加载事件的发生。

$E.on('preview-pane', 'load', function(){
    previewBody = $('preview-pane').contentWindow.document.getElementsByTagName('body')[0];
}
'preview-pane' 是我的 iframe 的 ID,我正在使用 YUI 来附加事件处理程序。但是,在我的回调函数中(当 iframe 加载时),尝试访问 body 会失败,我认为这是因为 iframe 在事件处理程序准备好之前就已经加载了。如果我通过让生成它的 PHP 脚本休眠来延迟 iframe 的加载,则此代码有效。
基本上,我想问的是在各种浏览器中检测 iframe 何时已加载并且其文档已准备好的正确方法是什么?

1
从YUI开始是个坏主意(以及任何其他JS库)。为什么?因为你可能无法知道那个库正在执行什么神奇的操作。如果它们不能完全支持“加载”,最好使用清晰易读的javascript自己实现。 - Christian
2
你经常可以阅读@Christian的库源代码。我发现这通常会给你很好的提示,无论你是否使用他们的实现方式。 - AJP
2
这要看你如何看待它。当时我知道YUI的事件处理程序可以正常工作,所以我不必担心代码的那部分。我也知道如何编写自己的addEvent处理程序,尽管与解决这个问题方面,它并没有比YUI更好。 - David Snabel-Caunt
似乎有点讽刺的是,即使问题和第二个得票最高的答案都只谈到jQuery,但jQuery却没有被提及。难道这个标签不应该存在吗? - Nico
1
@Nico 这个问题涉及到YUI和$是YUI的Dom包装器的别名。 - David Snabel-Caunt
显示剩余2条评论
6个回答

80
检测iframe何时加载完成并准备好文档? 最好的方法是让iframe内部的脚本直接调用父函数以告诉它自己已经准备就绪。在跨框架代码执行时,始终需要注意可能会按照您不希望的顺序发生事情。另一种选择是在其自己的作用域中设置“var isready = true;”,并使父脚本嗅探“contentWindow.isready”(如果没有添加onload处理程序)。 如果由于某种原因无法让iframe文档合作,则会出现传统的加载竞争问题,即使元素紧挨着彼此:
<img id="x" ... />
<script type="text/javascript">
    document.getElementById('x').onload= function() {
        ...
    };
</script>

不能保证在脚本执行时该项尚未加载完成。

避免加载竞争的方法有:

  1. 在IE上,可以使用“readyState”属性查看某些内容是否已经加载完成;

  2. 如果只有JavaScript可用时才有该项,则可以动态创建它,在设置源和附加到页面之前设置“onload”事件函数。在这种情况下,在回调函数设置之前无法加载它;

  3. 老式的方法是在标记中包含它:

    <img onload="callback(this)" ... />

HTML中的内联“onsomething”处理程序几乎总是错误的,并应该避免使用,但在这种情况下,有时这是最差的选择。


谢谢。我的解决方案检查readyState(如果存在),然后检查body元素的innerHTML长度以查看是否已加载。如果没有加载,则附加load事件处理程序。似乎工作正常。 - David Snabel-Caunt
10
内联事件并不是“不好”的,那只是当前的潮流。事实上,内联事件更容易调试和理解,不像它们的魔法对应物(相比之下,后者更容易附加、堆叠和无缝使用)。 - Christian
1
@Christian,当你需要将事件附加到非常长的列表的每个元素时,内联事件也是最佳选择。由于您已经在服务器端循环构建列表,因此附加内联事件更加高效。 - haknick
18
你是否希望在列表上使用单个事件监听器,并使用事件委托?这样会更高效。 - David Snabel-Caunt
这个解决方案可行:https://dev59.com/CXI-5IYBdhLWcg3w0cOG#11874986 如果你想使用干净的JavaScript,请选择间隔解决方案,并在间隔内使用同一线程中提到的方法之一访问iframe的内容。 - simo
4
如果 iframe 跨域,这种方法就行不通。iframe 内的脚本无法访问父窗口。 - Sudheer Someshwara

49

请查看这篇博客文章。它使用了jQuery,但即使您不使用它,也应该有所帮助。

基本上,您需要将以下内容添加到您的document.ready()

$('iframe').load(function() {
    RunAfterIFrameLoaded();
});

有趣的是,我遇到的问题与加载事件和时间有关。我正在按照那篇文章的建议监听加载事件。 - David Snabel-Caunt
我已经使用 console.log 尝试过了。它在 iframe 加载后被记录。 - shorif2000

14

对于使用React的开发者来说,检测同源iframe的加载事件非常简单,只需要在iframe元素上设置onLoad事件监听器即可。

<iframe src={'路径到iframe源'} onLoad={this.loadListener} frameBorder={0} />


onLoad事件只能触发同源iframe吗? - L_K

3

对于任何使用Ember的人,这应该按预期工作:

<iframe onLoad={{action 'actionName'}}  frameborder='0' src={{iframeSrc}} />

1
  1. 这段代码可以检测iframe内容何时加载完成 不需要jquery:

原文来源:Biranchi的回答

    // wait for the doc to load (wait for the class 'iframe...' to load) before sending the script
    checkIframeLoaded()
    
    function checkIframeLoaded() {
        // console.log(" function checked")
        // Get a handle to the iframe element
        var iframe = document.getElementsByClassName("docs-texteventtarget-iframe")[0];
        console.log("iframe", iframe)
        // check if the iframe is loaded or not (= undefined = null)
        if (iframe == null) {
            // If we are here, it is not loaded. Set things up so we check the status again in 1000 milliseconds
            window.setTimeout(checkIframeLoaded, 1000);
        } else {
            var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
            // Check if loading is completed
            if (iframeDoc.readyState == 'complete') {
    
                // The loading is complete, call the function we want executed once the iframe is loaded
                iframeDoc.addEventListener("keydown", dispatchkeyboard, false);
            } else {
                // even if the iframe is loaded the "readystate may not be completed yet" so we need to recall the function.
                window.setTimeout(checkIframeLoaded, 1000);
            }
        }
    }
    
  
  1. 这段代码可以与jquery一起使用:

来源: gilles bousquet的回答

    // use jquery to detect when iframe is loaded but doesn't work offline
    // (this code needs jQuery because it use the "$" below). if you use it; add this require in the ===usercript=== section
    //  @require    http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js

    var editingIFrame = $('iframe.docs-texteventtarget-iframe')[0];
   if (editingIFrame) {
        console.log("inside doc.getele")
        editingIFrame.contentDocument.addEventListener("keydown", dispatchkeyboard, false);
    }
    

0
jQuery(document).ready(function () {
    jQuery('iframe').load(function() {
        console.log(jQuery(this))
    });
})

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接