【问题标题】:dynamically created iframe triggers onload event twice动态创建的 iframe 触发 onload 事件两次
【发布时间】:2012-05-28 08:55:24
【问题描述】:

我动态创建了一个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?

【问题讨论】:

  • 我创建了一个 jsfiddle,以便您可以轻松地对其进行测试:jsfiddle.net/o34s8kk5/2 实际上,双重调用仅发生在 webkit 上

标签: javascript iframe


【解决方案1】:

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

如果您在 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 事件,也就是文档真正加载的事件。

【讨论】:

  • 这里也一样。在我有一个 onload="function1()" 的情况下,我有双重 onload 事件调用,并且在将它附加到页面后,我正在使用 jquery 执行一个('load',function2),导致对function1的双重调用在最新版本的 chrome 上......奇怪的东西。
  • 天哪,我已经处理这个问题大约六个月了。为什么这不是一个公认的答案? SO 之神应该自动接受对至少一岁的问题的高票数的答案。
【解决方案2】:

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

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

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

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

evt.target.src 在我的测试中只能在第一个 onload 调用中的 webkit 中为 ''(空字符串)。

不同情况下 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 显式设置为 about:blank 的 iframe 加载行为

// 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/";

在附加到 DOM 之前,iframe.src 显式设置为 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/";

【讨论】:

  • 我正在使用 DevExpress MVC,我看到 event.target.src 在第一个 load 上不是空字符串,而是一些 DevExpress 内部 url。
【解决方案3】:

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

<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'">

【讨论】:

    【解决方案4】:

    FWIW,虽然我能够在 iframe.srcjavascript: 的情况下重现双 onload,但我无法使用普通的 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
    

    【讨论】:

      【解决方案5】:

      这里的代码很好用!

      信用:@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>
      

      【讨论】:

        【解决方案6】:

        基于@jakub.g 的回答:

        从框架内部,evt.target.src hack 不起作用。但我发现我可以改为检查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 的旧版本

        【讨论】:

          【解决方案7】:

          我在 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>
          

          【讨论】:

            【解决方案8】:
            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")
              }  
                
              }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2014-06-18
              • 1970-01-01
              • 2010-10-23
              • 1970-01-01
              • 1970-01-01
              • 2012-10-10
              • 1970-01-01
              • 2016-09-13
              相关资源
              最近更新 更多