动态创建的iframe触发onload事件两次

问题描述 投票:0回答:9

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

javascript iframe
9个回答
72
投票

我也遇到了同样的问题,但是到处都没有答案,所以我自己测试了一下。

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


31
投票

扩展投票最高的答案:如果您无法控制事物何时以及如何附加到 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
只能在第一个
''
调用中的webkit中为
onload
(空字符串)。

不同情况下iframe onload行为研究

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

iframe onload 行为,在附加到 DOM 之前将

iframe.src
 显式设置为 JavaScript 表达式

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

2
投票
我在异步加载的样式表中就遇到过这种情况,就像这个。

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

0
投票
FWIW,虽然我能够在

onload

iframe.src
 的情况下重现双 
javascript:
,但我无法使用正常的 HTTP URL 重现它(加载发生了一次)。但是,当您更改调用顺序以便在附加到正文后设置 
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
    

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>
    

0
投票
基于@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

 的旧版本)
    


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>
    

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") } }
    

0
投票

Angular 的解决方案

发生这种情况是因为

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

使用

AfterViewInit hook 注册加载回调

模板:

<iframe #iframe [src]="src | safe : 'resourceUrl'" [height]="height" width="100%" ></iframe>
成分:

export class IframeWrapperComponent implements AfterViewInit { @Input({ required: true }) src = ''; @Input({ required: true }) title = ''; @Input({ required: true }) height = ''; @ViewChild('iframe') iFrame!: ElementRef<HTMLIFrameElement>; ngAfterViewInit(): void { if (this.iFrame) { this.iFrame.nativeElement.onload = () => { console.log('write your onload logic here'); }; } } }
    
© www.soinside.com 2019 - 2024. All rights reserved.