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 元素也有很好的兼容性。同時應避免對跨域的父子頁面交互。