Chrome Safari Firefox 中 IFRAME 元素在文檔樹中發生變化後父子頁面間的某些交互方式會失效...

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 內外的子頁面與父頁面交互的影響。

  1. window.onload 之前,通過 innerHTML 方法改變 IFRAME 元素在文檔樹中的位置:main1.html
  2. window.onload 之前,通過 removeChild、AppendChild 方法改變 IFRAME 元素在文檔樹中的位置:main2.html
  3. window.onload 之後,通過 innerHTML 方法改變 IFRAME 元素在文檔樹中的位置:main3.html
  4. 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.htmlmain2.htmlmain3.htmlmain4.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 方式則產生了差異:

  1. window.onload 之前,通過 innerHTML 方法改變 IFRAME 元素在文檔樹中的位置:
    • IE6 IE7 IE8 Firefox Opera中,子頁面均可以獲得移動後的【ifr1】及【ifr2】相關對象;
    • Chrome Safari中,雖然可以獲取到父頁面的【ifr1】及【ifr2】相關對象,但是與 IE Firfox Opera 中不同,該對象類型爲 "HTMLIframeElement" ,而不是 "Window" ,所以該對象的子對象無法獲取。
  2. window.onload 之前,通過 removeChild、AppendChild 方法改變 IFRAME 元素在文檔樹中的位置:
    • 各瀏覽器中,子頁面均可以獲得移動後的【ifr1】及【ifr2】相關對象。
  3. 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" ,所以該對象的子對象無法獲取。
  4. 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 元素也有很好的兼容性。同時應避免對跨域的父子頁面交互。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章