37

我动态创建了一个 iframe,发现这个 iframe 触发了两次 onload 事件。

var i=0;
frameOnload=function(){
  console.log(i++);  
};

var ifr=document.createElement("iframe");  
ifr.src="javascript:(function(){document.open();document.write('test');document.close();})();";
ifr.onload=frameOnload;  
document.body.appendChild(ifr);

为什么我终于是1?
如何防止 iframe 的 onload 两次,而不是将 onload 函数指向自身内部的 null?

4

8 回答 8

66

我也遇到了同样的问题,但没有得到任何答案,所以我自己测试了。

如果在 iframe 附加到正文之前附加 onload 事件,则 iframe onload 事件将在 webkit 浏览器 ( safari/chrome ) 中触发两次。

因此,您可以通过以下方式更改代码来防止 iframe 加载两次。

document.body.appendChild(ifr);
ifr.onload=frameOnload; // attach onload event after the iframe is added to the body

然后,您只会得到一个 onload 事件,即文档真正加载的事件。

于 2013-04-08T13:33:40.497 回答
23

扩展投票最多的答案:如果您无法控制将事物附加到 DOM 的时间和方式——例如,在使用框架时(我们在 Angular 应用程序中发生了这种情况)——您可能想要尝试以下解决方案。

我进行了繁重的跨浏览器测试,发现了以下解决方法:检查传递给onload回调的参数并evt.target.src在事件侦听器中进行检查。

iframe.onload = function(evt) {
    if (evt.target.src != '') {
        // do stuff
    }
}

(如果您从 HTML 调用全局方法,请记住传入event您的 HTML 标记<iframe onload="window.globalOnLoad(event)">:)

evt.target.src根据我的测试,在第一次调用''中只能在 webkit 中是(空字符串)。onload

不同情况下 iframe 加载行为的研究

iframe.onload = function(evt){
    console.log("frameOnload", ++i);
    console.log("src = '" + evt.target.src + "'");
};

具有常规 URL 的 iframe 加载行为

// Chrome:  onload 1 src='', onload 2 src=requestedSrc
// IE11:    onload 1 src=requestedSrc
// Firefox: onload 1 src=requestedSrc
document.body.appendChild(iframe);
iframe.src = "http://www.example.org/";

带有 404 URL 的 iframe 加载行为

// Chrome:  onload 1 src='', onload 2 src=requestedSrc
// IE11:    onload 1 src=requestedSrc
// Firefox: onload 1 src=requestedSrc
document.body.appendChild(iframe);
iframe.src = "http://www.example.org/404";

具有不可解析(在 DNS 级别)URL 的 iframe 加载行为

// Chrome:  onload 1 src='', onload 2 src=requestedSrc
// IE11:    onload 1 src=requestedSrc
// Firefox: onload NEVER triggered!
document.body.appendChild(iframe);
iframe.src= 'http://aaaaaaaaaaaaaaaaaaaaaa.example/';

在附加到 DOM 之前iframe.src显式设置为的iframe 加载行为about:blank

// Chrome:  onload 1 src='about:blank', onload 2 src=requestedSrc
// IE11:    onload 1 src=requestedSrc
// Firefox: onload 1 src=requestedSrc
iframe.src = "about:blank";
document.body.appendChild(iframe);
iframe.src = "http://www.example.org/";

iframe.src在附加到 DOM 之前显式设置为 javascript 表达式的iframe 加载行为

// Chrome:  onload 1 src='javascript:void 0', onload 2 src=requestedSrc
// IE11:    onload 1 src=requestedSrc
// Firefox: onload 1 src=requestedSrc
iframe.src = "javascript:void 0";
document.body.appendChild(iframe);
iframe.src = "http://www.example.org/";
于 2016-07-19T13:10:58.800 回答
1

我在异步加载的样式表上遇到过这种情况,比如这个。

<link rel="preload" href="stylesheet.css" as="style" onload="this.rel='stylesheet'">

我发现最简单的解决方案是让事件自行删除:

<link rel="preload" href="stylesheet.css" as="style" onload="this.removeAttribute('onload'); this.rel='stylesheet'">
于 2018-11-29T10:15:45.763 回答
0

FWIW,虽然我能够onload在 where iframe.srcis的情况下重现双倍javascript:,但我无法使用普通的 HTTP URL 重现它(onload 发生了一次)。但是,当您更改调用顺序以便iframe.src在附加到正文之后设置时,就会发生双重加载(同样,仅限 webkit):

var i = 0;

var iframe = document.createElement("iframe");
iframe.onload = function(){
  console.log("frameOnload", ++i);
};
document.body.appendChild(iframe);
iframe.src = "http://www.example.org/";
//iframe.src = "http://www.example.org/404";

// double onload!

当我在附加之前分配src时,我接到一个onload电话。

var i = 0;

var iframe = document.createElement("iframe");
iframe.onload = function(){
  console.log("frameOnload", ++i);
};
iframe.src = "http://www.example.org/";
//iframe.src = "http://www.example.org/404";
document.body.appendChild(iframe);

// only one onload
于 2016-07-19T08:34:23.433 回答
0

这里的代码很好用!

信用:@Dave Stolie https://coderanch.com/t/443223/languages/Frames-loading-refresh

<html>  
 <head>  
   <script type="text/javascript">  
     function loadFrame()  
     {
        var url="http://www.google.com";
        alert(document.getElementById("theFrame").src);

        if (document.getElementById("theFrame").src != url)
        {
            alert("loading");
            document.getElementById("theFrame").src = url;  
        }
     }  
   </script>  
   </head>  
   <body onLoad="loadFrame();">  
   <iframe id="theFrame" src="x"/>
  </body>  
  </html>
于 2019-06-19T08:02:18.560 回答
0

基于@jakub.g 的回答:

从框架内部,evt.target.srchack 不起作用。但我发现我可以改为检查evt.target.title. 第二次DOMContentLoaded被触发,evt.target指向正在加载的文档,所以这会起作用:

document.addEventListener('DOMContentLoaded', function(evt) {
  if (evt.target.title !== myPageTitle) {
    // Work around for Webkit browsers trigging this event twice om iframes
    return
  }
  // DOM content loaded, for real
}

即使在 Internet Explorer 中,这种行为似乎也是一致的(保存旧版本没有.addEventListener

于 2020-06-08T14:59:21.083 回答
0

我在 vue.js 2.6.12 中遇到了这个问题,
我通过检查 event.target.url 是否与 iframe 的 onload 函数中的 src url 匹配来处理它。

<template>
           <iframe
                @load="onLoad"
                :src="embedUrl"
                ref="cardNumber"
                frameborder="0"
                scrolling="no"
                width="100%"
                height="100%"
                align="left"
            ></iframe>
</template>


<script>
export default {
...,
methods: {
onLoad (event) {
    // considerting iframe to be loaded if the event.target.url matches the src url
    if(event.target.src === this.embedUrl)) {
      // instructions after loading
    } 
   }
 }
}
</script>
于 2021-02-26T12:47:45.343 回答
0
Register for onload event after appending iframe. This solves my problem.
document.body.appendChild(ifr);
ifr.onload=frameOnload;  

E.G:
iframeContainer.appendChild(iframe)
  iframe.onload = function() {
    console.log("iframe onload called")
  iframeContainer.appendChild(iframe)
  iframe.onload = function() {
    console.log("iframe onload called")
  }  
    
  }
于 2021-03-18T06:36:32.990 回答