关于 javascript:Firefox 没有为动态嵌套 iframe 触发 onload

Firefox not firing onload for dynamic nested iframe

我有以下代码可以在 Chrome 和 MS Edge 上运行,但不能在 Firefox 上运行。

Parent.html 有这个脚本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<html>
<body>

var ifr1 = document.createElement('iframe');

ifr1.onload = function() {
    alert("iframe 1 loaded") //fires on all browsers
    script = ifr1.contentWindow.document.createElement('script');
    script.src = 'PATH/TO/script.js';
    script.onload = function() {
        alert("script 1 onload") //fires on all browsers
    };
    ifr1.contentWindow.document.body.appendChild(script);
};
document.body.appendChild(ifr1);


</body>
</html>

它创建一个 iframe 并在该 iframe 中加载 script.js。

这里是 script.js,它和上面做同样的事情 -

1
2
3
4
5
6
7
8
9
10
11
12
var ifr2 = document.createElement('iframe');

ifr2.onload = function() {
    alert("iframe 2 loaded") //doesn't fire on Firefox
    script = ifr2.contentWindow.document.createElement('script');
    script.src = 'https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js';
    script.onload = function() {
        alert("script 2 onload")
    };
    ifr2.contentWindow.document.body.appendChild(script);
};
document.body.appendChild(ifr2);

它在 Parent.html 创建的 iframe ifr1 中创建另一个 iframe ifr2

现在,Chrome 和 Edge 会正确显示所有警报,但 Firefox 不会为 ifr1 中加载的 ifr2 触发 onload 事件(甚至 IE 也会为 ifr2 触发 onload)。知道为什么吗?


一种解决方法是创建一个简单的 html 页面 - 甚至只是 <html><head></head><body></body></html>(甚至可能更少).. 让我们称之为 empty.html

如果您现在将 Parent.html 更改为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<html>
<body>

var ifr1 = document.createElement('iframe');

ifr1.onload = function() {
    alert("iframe 1 loaded") //fires on all browsers
    script = ifr1.contentWindow.document.createElement('script');
    script.src = 'PATH/TO/script.js';
    script.onload = function() {
        alert("script 1 onload") //fires on all browsers
    };
    ifr1.contentWindow.document.body.appendChild(script);
};
ifr1.src = 'empty.html'; // add this code *******
document.body.appendChild(ifr1);


</body>
</html>

然后它按预期工作

不知道为什么 firefox 会这样做——这可能是因为没有 src 属性,iframe\\ 的位置是 about:blank——但是为什么第一个有效而不是第二个有效是一个谜我

edit: 嗯,它做了一次然后又停止了!!

好的,真的很奇怪 - 如果你添加 ifr1.src = 'empty.html' 它可以工作......如果你在 script.js 中复制它,它会再次中断

不确定我是否已经很好地回答了你,但至少我给了你一个有效的工具:p


虽然这是一种不好的做法,并且在 chrome 中已被弃用(它会在控制台中显示警告。
在附加脚本标记之前添加这一行为我解决了这个问题:

iframe.contentWindow.document.write('<html><head></head><body></body></html>');

看来 firefox 需要它来初始化 iframe 并运行 javascript...


IMO 这是 Firefox 中的一个错误,因为 Firefox 不会按照您期望的方式在 iframe 中执行 load 事件。 Mozilla 的开发人员可能不同意。这一切都取决于您对"加载"应该意味着什么的定义,它比您想象的要复杂。

在任何情况下,真正的问题是何时知道访问 ifr2.contentWindow 是安全的,而使用 ifr2.onload 的全部意义在于您可以确定何时存在 ifr2.contentWindow

但是请注意,contentWindow 和文档会在 document.body.appendChild(ifr2) 执行时立即创建,因此如果您将 appendChild() 调用移至当前代码上方并删除 onload 函数,它将按预期工作。

或者您可以将 onload 函数更改为其他函数并在 appendChild() 之后直接执行它,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var ifr2 = document.createElement('iframe');
function loadit() {
    console.log("iframe 2 loaded")
    script = ifr2.contentWindow.document.createElement('script');
    script.src = '...';
    script.onload = function() {
        console.log("script 2 onload")
    };
    ifr2.contentWindow.document.body.appendChild(script);
};

// add the iframe to the document
document.body.appendChild(ifr2);

// now execute your script stuff:
loadit()

这应该适用于所有(现代)浏览器。