window 对象中的 frames 集合可以返回当前 window 中的子框架列表,这是一个类似数组的集合对象。可以通过整型下标或者子框架元素的 name 属性获取到该集合内对应的子框架 window 对象。
IFRAME 元素对应的 DOM 对象为 HTMLIframeElement,各浏览器均支持 HTMLIframeElement 接口中的 contentWindow 属性,这个属性返回 IFRAME 引入子页面的 window 对象。
假设在当前父页面中存在一个 id 和 name 属性为 "ifr" 的 IFRAME 对象,则可以通过 window.frames["ifr"] 或者 document.getElementById("ifr").contentWindow 这两组方法获取到 IFRAME 引入页面的 window 对象。这两种方法在所有主流浏览器中均有很好的兼容性。但是却并不符合 W3C 规范。其中 window.frames 集合算 DOM Level 0 范畴,而 contentWindow 属性为 IE5.5 引入。
下面分 4 种情况测试当 IFRAME 元素在文档树中发生变化对 IFRAME 内外的子页面与父页面交互的影响。
- window.onload 之前,通过 innerHTML 方法改变 IFRAME 元素在文档树中的位置:main1.html。
- window.onload 之前,通过 removeChild、AppendChild 方法改变 IFRAME 元素在文档树中的位置:main2.html。
- window.onload 之后,通过 innerHTML 方法改变 IFRAME 元素在文档树中的位置:main3.html。
- window.onload 之后,通过 removeChild、AppendChild 方法改变 IFRAME 元素在文档树中的位置:main4.html。
测试代码如下:
main1.html
<style>
iframe { width:450px; height:1800px; }
</style>
before window.onload, innerHTML
<div id="div1">
<iframe id="ifr1" name="ifr1" src="sub.html"></iframe>
<iframe id="ifr2" name="ifr2" src="0.html"></iframe>
</div>
<div id="div2">
</div>
<script>
function $(id) {
return document.getElementById(id);
}
$("div2").innerHTML = $("div1").innerHTML;
$("div1").innerHTML = "";
</script>
|
main2.html
<style>
iframe { width:450px; height:1800px; }
</style>
before window.onload, appendChild
<div id="div1">
<iframe id="ifr1" name="ifr1" src="sub.html"></iframe>
<iframe id="ifr2" name="ifr2" src="0.html"></iframe>
</div>
<div id="div2">
</div>
<script>
function $(id) {
return document.getElementById(id);
}
var iframe1 = $("div1").children[0];
var iframe2 = $("div1").children[1];
$("div1").removeChild(iframe1);
$("div1").removeChild(iframe2);
$("div2").appendChild(iframe1);
$("div2").appendChild(iframe2);
</script>
|
main3.html
<style>
iframe { width:450px; height:1800px; }
</style>
after window.onload, innerHTML
<div id="div1">
<iframe id="ifr1" name="ifr1" src="sub.html"></iframe>
<iframe id="ifr2" name="ifr2" src="0.html"></iframe>
</div>
<div id="div2">
</div>
<script>
function $(id) {
return document.getElementById(id);
}
window.onload = function () {
$("div2").innerHTML = $("div1").innerHTML;
$("div1").innerHTML = "";
}
</script>
|
main4.html
<style>
iframe { width:450px; height:1800px; }
</style>
after window.onload, appendChild
<div id="div1">
<iframe id="ifr1" name="ifr1" src="sub.html"></iframe>
<iframe id="ifr2" name="ifr2" src="0.html"></iframe>
</div>
<div id="div2">
</div>
<script>
function $(id) {
return document.getElementById(id);
}
window.onload = function () {
var iframe1 = $("div1").children[0];
var iframe2 = $("div1").children[1];
$("div1").removeChild(iframe1);
$("div1").removeChild(iframe2);
$("div2").appendChild(iframe1);
$("div2").appendChild(iframe2);
}
</script>
|
sub.html
<html>
<head>
<style>
* { font:12px Arial; }
body { margin:0; }
span { font-weight:bold; }
.g { color:green; }
.r { color:red; }
</style>
</head>
<body>
<dl>
<script>
function myEval (code) {
var script = document.createElement("script");
script.text = "var ret =" + code;
document.body.appendChild(script);
var x = ret;
document.body.removeChild(script);
return x;
}
function tryObj (obj_text) {
var ok = '<span class="g">OK</span>';
var fail = '<span class="r">FAIL</span>'
try {
var f = "";
var ev = myEval(obj_text);
try {
f = ev.toString();
} catch(e) {
f = "";
}
return ev ? ok + " " + f : fail + " " + f;
} catch(e) {
return fail + " " + f;
}
}
var arr = [
'parent',
'parent.document',
'parent.document.getElementById("ifr1")',
'parent.document.getElementById("ifr1").contentWindow',
'parent.document.getElementById("ifr1").contentWindow.document',
'parent.document.getElementById("ifr1").contentWindow.history',
'parent.document.getElementById("ifr1").contentWindow.location',
'parent.document.getElementById("ifr1").contentWindow.navigator',
'parent.document.getElementById("ifr1").contentWindow.screen',
'parent.document.getElementById("ifr1").contentWindow.alert',
'parent.document.getElementById("ifr2")',
'parent.document.getElementById("ifr2").contentWindow',
'parent.document.getElementById("ifr2").contentWindow.document',
'parent.document.getElementById("ifr2").contentWindow.history',
'parent.document.getElementById("ifr2").contentWindow.location',
'parent.document.getElementById("ifr2").contentWindow.navigator',
'parent.document.getElementById("ifr2").contentWindow.screen',
'parent.document.getElementById("ifr2").contentWindow.alert',
'parent.frames',
'parent.frames["ifr1"]',
'parent.frames["ifr1"].document',
'parent.frames["ifr1"].history',
'parent.frames["ifr1"].location',
'parent.frames["ifr1"].navigator',
'parent.frames["ifr1"].screen',
'parent.frames["ifr1"].alert',
'parent.frames["ifr2"]',
'parent.frames["ifr2"].document',
'parent.frames["ifr2"].history',
'parent.frames["ifr2"].location',
'parent.frames["ifr2"].navigator',
'parent.frames["ifr2"].screen',
'parent.frames["ifr2"].alert'
];
for (var i in arr) {
document.write('<dt>' + arr[i] + ':</dt>');
document.write('<dd>' + tryObj(arr[i]) + '</dd>');
}
</script>
</dl>
</body>
</html>
|
0.html
<html></html> |
上面代码中有 4 个主页面 main1.html、main2.html、main3.html、main4.html,分别对应本文分析的 4 中情况,每组代码均包含两个 DIV 元素【div1】与【div2】,其中初始状态【div1】包含 IFRAME 元素【ifr1】及【ifr2】,【div2】中为空。
【ifr1】引入了子页面 "sub.html" ,【ifr2】引入了子页面 "0.html" 。通过 JavaScript 将【div1】中的【ifr1】及【ifr2】移动到【div2】内。但是 4 个主页面采取了不同的移动方式。
子页面中,分别判断了 33 个对象的状态,若存在该对象,则显示“OK" 及对象类型,否则显示 "FAIL" 。
在本地构建 Web 服务器1,将测试代码放入服务器进行测试。
| window.onload 之前,通过 innerHTML 方法改变 IFRAME 元素在文档树中的位置 | window.onload 之前,通过 removeChild、AppendChild 方法改变 IFRAME 元素在文档树中的位置 | window.onload 之后,通过 innerHTML 方法改变 IFRAME 元素在文档树中的位置 | window.onload 之后,通过 removeChild、AppendChild 方法改变 IFRAME 元素在文档树中的位置 | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| IE6 IE7 IE8 Opera | Firefox | Chrome Safari | IE6 IE7 IE8 Opera | Firefox | Chrome Safari | IE6 IE7 IE8 Opera | Firefox | Chrome Safari | IE6 IE7 IE8 Opera | Firefox | Chrome Safari | |
| parent | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK |
| parent.document | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK |
| parent.document .getElementById("ifr1") | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK |
| parent.document .getElementById("ifr1").contentWindow | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK |
| parent.document .getElementById( "ifr1") .contentWindow.document | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK |
| parent.document .getElementById("ifr1") .contentWindow.history | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK |
| parent.document .getElementById("ifr1") .contentWindow.location | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK |
| parent.document .getElementById("ifr1") .contentWindow.navigator | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK |
| parent.document .getElementById("ifr1") .contentWindow.screen | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK |
| parent.document .getElementById("ifr1") .contentWindow.alert | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK |
| parent.document .getElementById("ifr2") | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK |
| parent.document .getElementById("ifr2") .contentWindow | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK |
| parent.document .getElementById("ifr2") .contentWindow.document | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK |
| parent.document .getElementById("ifr2") .contentWindow.history | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK |
| parent.document .getElementById("ifr2") .contentWindow.location | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK |
| parent.document .getElementById("ifr2") .contentWindow.navigator | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK |
| parent.document .getElementById("ifr2") .contentWindow.screen | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK |
| parent.document .getElementById("ifr2") .contentWindow.alert | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK |
| parent.frames | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK |
| parent.frames["ifr1"] | OK | OK | OK2 | OK | OK | OK | OK | OK | OK2 | OK | OK | OK |
| parent.frames["ifr1"].document | OK | OK | FAIL | OK | OK | OK | OK | FAIL | FAIL | OK | FAIL | OK |
| parent.frames["ifr1"].history | OK | OK | FAIL | OK | OK | OK | OK | OK | FAIL | OK | OK | OK |
| parent.frames["ifr1"].location | OK | OK | FAIL | OK | OK | OK | OK | OK | FAIL | OK | OK | OK |
| parent.frames["ifr1"].navigator | OK | OK | FAIL | OK | OK | OK | OK | OK | FAIL | OK | OK | OK |
| parent.frames["ifr1"].screen | OK | OK | FAIL | OK | OK | OK | OK | OK | FAIL | OK | OK | OK |
| parent.frames["ifr1"].alert | OK | OK | FAIL | OK | OK | OK | OK | OK | FAIL | OK | OK | OK |
| parent.frames["ifr2"] | OK | OK | OK2 | OK | OK | OK | OK | OK | OK2 | OK | OK | OK |
| parent.frames["ifr2"].document | OK | OK | FAIL | OK | OK | OK | OK | FAIL | FAIL | OK | FAIL | OK |
| parent.frames["ifr2"].history | OK | OK | FAIL | OK | OK | OK | OK | OK | FAIL | OK | OK | OK |
| parent.frames["ifr2"].location | OK | OK | FAIL | OK | OK | OK | OK | OK | FAIL | OK | OK | OK |
| parent.frames["ifr2"].navigator | OK | OK | FAIL | OK | OK | OK | OK | OK | FAIL | OK | OK | OK |
| parent.frames["ifr2"].screen | OK | OK | FAIL | OK | OK | OK | OK | OK | FAIL | OK | OK | OK |
| parent.frames["ifr2"].alert | OK | OK | FAIL | OK | OK | OK | OK | OK | FAIL | OK | OK | OK |
注1. Chrome 中认为本地页面为跨域,IFRAME 元素父子页面之间的脚本交互是不安全的,会在控制台提示错误:Unsafe JavaScript attempt to access frame with URL [XXX] from frame with URL [XXX]. Domains, protocols and ports must match.
注2. 与其他浏览器不同,Chrome 和 Safari 此时虽然返回一个有效对象,但此对象类型不是 [window] 而是 [HTMLIframeElement]。
从上表中的结果可见,通过 document.getElementById("IFRAME").contentWindow 的方式获取 IFRAME 元素引入子页面的 window 对象,各浏览器均没有任何问题。而 window.frames 方式则产生了差异:
- window.onload 之前,通过 innerHTML 方法改变 IFRAME 元素在文档树中的位置:
- 在 IE6 IE7 IE8 Firefox Opera 中,子页面均可以获得移动后的【ifr1】及【ifr2】相关对象;
- 在 Chrome Safari 中,虽然可以获取到父页面的【ifr1】及【ifr2】相关对象,但是与 IE Firfox Opera 中不同,该对象类型为 "HTMLIframeElement" ,而不是 "Window" ,所以该对象的子对象无法获取。
- window.onload 之前,通过 removeChild、AppendChild 方法改变 IFRAME 元素在文档树中的位置:
- 各浏览器中,子页面均可以获得移动后的【ifr1】及【ifr2】相关对象。
- window.onload 之后,通过 innerHTML 方法改变 IFRAME 元素在文档树中的位置:
- 在 IE6 IE7 IE8 Opera 中,子页面均可以获得移动后的【ifr1】及【ifr2】相关对象;
- 在 Firefox 中,在 window.onload 中移动 IFRAME 却使 IFRAME 的 window 对象中 "document" 对象失效;
- 在 Chrome Safari 中,虽然可以获取到父页面的【ifr1】及【ifr2】相关对象,但是与 IE Firefox Opera 中不同,该对象类型为 "HTMLIframeElement" ,而不是 "Window" ,所以该对象的子对象无法获取。
- window.onload 之后,通过 removeChild、AppendChild 方法改变 IFRAME 元素在文档树中的位置:
- 在 IE6 IE7 IE8 Opera Chrome Safari 中,子页面均可以获得移动后的【ifr1】及【ifr2】相关对象;
- 在 Firefox 中,在 window.onload 中移动 IFRAME 却使 IFRAME 的 window 对象中“document" 对象失效。
可见,对于 window.frames 方式,使用情况 2,即 window.onload 之前,通过 removeChild、AppendChild 方法改变 IFRAME 元素在文档树中的位置不会出现兼容性问题。
解决方案
根据上面所得的结果,推荐使用 document.getElementById("IFRAME").contentWindow.document 获取 IFRAME 元素内页面的 document 对象,且对于在文档树中移动位置后的 IFRAME 元素也有很好的兼容性。同时应避免对跨域的父子页面交互。
本文通过具体测试案例,分析了不同浏览器环境下IFRAME元素移动前后对其内部页面交互的影响,探讨了window.frames与contentWindow两种方法的兼容性。

2276

被折叠的 条评论
为什么被折叠?



