iframe的onload和 addEventListener('load')行为差异解析

21

我一直在尝试向页面添加隐藏的iframe元素,并且想要在它们加载完成后操作这些元素的DOM。我注意到,我不能在将iframe添加到页面后立即开始操作DOM,因为它还没有加载完成。这不能通过DOMContentLoaded事件来完成,因为该事件针对的是文档,而在将iframe添加到页面之前,文档并不存在于其内部,所以我们必须使用load事件。

以下是一些测试代码:

var iframe = document.createElement('iframe');
iframe.onload = function() { console.log('loaded!'); };
document.getElementsByTagName('body')[0].appendChild(iframe);

这段代码按预期工作,但是当我将它更改为addEventListener时,它甚至没有被添加到DOM中:

var iframe = document.createElement('iframe');
iframe.addEventListener('load', function() { console.log('loaded!'); });
document.getElementsByTagName('body')[0].appendChild(iframe);

我没有在IE中测试过attachEvent

有人能解释一下吗?

3个回答

23

addEventListener()函数需要3个参数!请查看https://developer.mozilla.org/en/DOM/element.addEventListener

第三个参数被标记为可选,但是他们写道:

请注意,在所有浏览器版本中,此参数并非都是可选的。

我不确定何时和何地需要它,但是我的FF4测试在使用2个参数调用addEventListener时抛出异常:

未捕获的异常:[Exception... "Not enough arguments" nsresult: "0x80570001 (NS_ERROR_XPC_NOT_ENOUGH_ARGS)" location: "JS frame :: http://localhost/index.php :: :: line 10" data: no]

顺便说一句,您的代码在Chrome中运行良好[控制台中记录了字符串loaded!]。

与FF一样,IE9在标准模式下(使用<!DOCTYPE html>)需要第三个参数。 IE9是支持W3C事件模型的第一个IE。因此,在早期版本中,我们需要尝试使用attachEvent。我没有早期的IE,但它在IE7/8标准模式甚至在IE9的怪异模式下都可以工作。这是我使用的代码:

<!DOCTYPE html>
<html>
<head><title></title></head>
<body>
<script>
    window.onload=function(){
        var iframe = document.createElement('iframe');
        var func = function() { console.log('loaded!');};
        if(iframe.addEventListener)
            iframe.addEventListener('load', func, true);
        else if(iframe.attachEvent)
            iframe.attachEvent('onload',func);
        document.body.appendChild(iframe);
    }
</script>
</body>
</html>

我相信你提到的第三个参数正是问题所在,我没有收到你的错误信息,但可能是被某种方式压制了。我不记得了,但显然我没有在Chrome中测试它(我太傻了...)。干得好,谢谢。 - roryf

1

这对我有效:

HTML:

iframe source code: <br />
<textarea id="output" rows="20" cols="60">loading ...</textarea>

JavaScript(在文档准备就绪时):

var iframe = document.createElement('iframe');
iframe.id = iframe.name = "testframe";
iframe.src = "http://fiddle.jshell.net";
iframe.width = 400;
iframe.height = 100;
iframe.style.display = "none";

if (iframe.addEventListener)
    iframe.addEventListener("load", loaded, false);
else
    iframe.attachEvent("onload", loaded);

function loaded() {
    var html;
    if (iframe.contentDocument)
        html = iframe.contentDocument.getElementsByTagName("HTML")[0].innerHTML;
    else
        html = window.frames[iframe.name].document.getElementsByTagName("html")[0].innerHTML;

    document.getElementById("output").value = html;
}

document.getElementsByTagName("body")[0].appendChild(iframe);

查看演示:http://jsfiddle.net/WcKEz/

适用于addEventListener,但包括降级到attachEvent。当然,只能在相同域上访问IFRAME的DOM。


0

第一个例子有效吗?不确定您要寻找什么,但这应该阐明事件何时起作用:jQuery document.ready source

$(document).ready equivalent without jQuery

addEventListener在IE中不起作用,因此如果您正在测试那里,则第二个将在iframe附加之前失败。

您还可以从页面本身添加回调,例如使用jQuery(以便您不必重新发明轮子),我怀疑$(iframe).ready() {..}会给您一致的行为。


除非我漏掉了什么,否则无法使用“ready”事件,因为这需要一个尚不存在的文档。 - roryf
onload 看起来没问题:http://www.w3schools.com/jsref/event_frame_onload.asp 我有点错过了你正在动态构建整个东西的事实。看看这个...虽然有点老,但也许会有所帮助。http://bytes.com/topic/javascript/answers/94228-iframe-dynamic-create-populate-immediately - Jamie Treworgy
坦白地说,我会更相信早上的便便比w3schools(太过分了?是的,太过分了......)。 但是,另一个链接看起来很有趣 - 这个人使用了document.write,我怀疑几年前在项目中使用过。 现在只要我能找到那段代码就好了:( - roryf
2
哈哈!好吧,虽然我认为他的技术看起来还不错(作为测试访问文档的不同方式的一种方法)。这里有一篇更详细的博客文章,由一个貌似已经高中毕业的人撰写,与此类似:http://thomas.bindzus.me/2007/12/24/adding-dynamic-contents-to-iframes/...似乎没有什么真正干净的方法来做到这一点。 - Jamie Treworgy
另一个有趣的链接。虽然没有回答我最初的问题,即为什么一个事件处理程序有效而另一个无效,但可能提供一个解决方案,从而消除了对事件处理程序的需求。谢谢! - roryf
显示剩余2条评论

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