BX9029: IE 和 Firefox 可以通過特定方法使 innerHTML 方法載入的 SCRIPT 標簽中的 JavaScript 代碼在頁面加載后也可以執行


標准參考

根據 W3C HTML4.01 規范中的描述,SCRIPT 標簽內的 "腳本" 只會在頁面加載時執行一次,或者通過綁定事件實現在頁面加載后腳本能夠重復地執行。

defer 屬性是 SCRIPT 元素的特有屬性,這是一個布爾型屬性,它通知用戶端這段腳本中不會生產文檔內容(如 "documnet.write" ),所以不必現在立即執行,一般的擁有 defer 屬性的 SCRIPT 元素中的腳本會較晚的被執行。

關於 SCRIPT 元素的詳細資料,請參考 HTML4.01 規范 18 中的內容。

關於 defer 屬性的詳細資料,請參考 HTML4.01 規范 18.2.1 中的內容。

問題描述

在 IE6 IE7 IE8 中,當使用 innerHTML 方法插入腳本時,SCRIPT 元素必須設置 defer 屬性。
在 Firefox 中,先將被插入 HTML 代碼的元素從其父元素中移除,然后使用 innerHTML 插入包含 SCRIPT 元素的代碼,最后將這個元素恢復至原父元素中,則經過此操作后 SCRIPT 中的腳本可以被執行。

造成的影響

若利用某些瀏覽器的特性迫使通過 innerHTML 方法載入的 SCRIPT 標簽內的腳本執行已達到某些目的,則在 IE、Firefox 之外的瀏覽器中腳本將不被執行,從而造成各種兼容性問題。

受影響的瀏覽器

IE6 IE7 IE8
使用 innerHTML 方法插入腳本時,SCRIPT 元素必須設置 defer 屬性。

Firefox
先將被插入 HTML 代碼的元素從其父元素中移除,然后使用innerHTML插入包含SCRIPT元素的代碼,最后將這個元素恢復至原父元素中,則經過此操作后SCRIPT中的腳本可以被執行。

問題分析

所有瀏覽器中,默認情況下通過 innerHTML 方法動態插入到頁面中的 SCRIPT 標簽中的腳本代碼均不能被執行。

分析以下代碼:

<html>
<head>
</head>
<body>
<div id="d">Some text</div>
<script>
    var a = "<div>a DIV</div><script>alert('alert');<\/script>";
    document.getElementById("d").innerHTML = a;
</script>
</body>
</html>

上面代碼中 DIV 元素【d】的初始內容為 "Some text" ,隨后通過 innerHTML 方法將【d】中內容替換為一個 DIV 元素及一個 SCRIPT 元素,SCRIPT 元素內包含一段 JavaScript 腳本。

這段代碼在所有瀏覽器中均沒有執行 SCRIPT 元素中的腳本。
這是因為根據 W3C 規范,SCRIPT 標簽中所指的腳本僅在瀏覽器第一次加載頁面時對其進行解析並執行其中的腳本代碼,所以通過 innerHTML 方法動態插入到頁面中的 SCRIPT 標簽中的腳本代碼在所有瀏覽器中默認情況下均不能被執行。

下面分析 IE 中使 innerHTML 插入 HTML 代碼的腳本執行的特殊方法及其 Bug:

MSDN 中關於 innerHTML 方法中提到:

當使用 innerHTML 方法插入腳本時,SCRIPT 元素必須設置 defer 屬性。

defer1.html

<html>
<head>
</head>
<body>
<script defer>
	alert("with defer, run later.");
	document.write("with defer, run later.");
</script>
<script>
	alert("without defer, run immediately.");
	document.write("without defer, run immediately.");
</script>
</body>
</html>

defer2.html

<html>
<head>
</head>
<body>
<script defer src="defer.js"></script>
<script src="normal.js"></script>
</body>
</html>

defer.js

alert("with defer, run later.");
document.write("with defer, run later.");

normal.js

alert("without defer, run immediately.");
document.write("without defer, run immediately.");

上例中兩段測試代碼中在各瀏覽器中運行效果如下:

IE
Firefox
Chrome, Safari, Opera

defer1.html
alert順序:without defer, run immediately. -> with defer, run later.
頁面輸出:with defer, run later.
alert順序:with defer, run later. -> without defer, run immediately.
頁面輸出:with defer, run later. without defer, run immediately.

defer2.html
alert順序:without defer, run immediately. -> with defer, run later.
頁面輸出:with defer, run later.
alert順序:without defer, run immediately. -> with defer, run later.
頁面輸出:without defer, run immediately.
alert順序:with defer, run later. -> without defer, run immediately.
頁面輸出:with defer, run later. without defer, run immediately.

由上表可知,

  • IE 對於 SCRIPT 元素內的腳本即由 SCRIPT 元素引入的腳本文件均具備延遲運行的效果。
  • Firefox 僅對通過 SCRIPT 元素引入的腳本文件具有延遲運行效果。但會忽略 document.write 語句。
  • Chrome Safari Opera 不支持 defer 屬性。

分析以下代碼:

<html>
<head>
</head>
<body>
<div id="d1"></div>
<div id="d2"></div>
<script>
    var a1 = "<div>a1</div><script defer>alert('a1');<\/script>";
    var a2 = "<script defer>alert('a2');<\/script>"
    document.getElementById("d1").innerHTML = a1;
    document.getElementById("d2").innerHTML = a2;
</script>
</body>
</html>

上面代碼中分別往【d1】和【d2】中通過 innerHTML 插入了一段 HTML 代碼,且均包含有 SCRIPT 標簽,SCRIPT 標簽設置了 defer 屬性。區別為【d1】中插入的 HTML 代碼比【d2】中在最開始多了一個 DIV 元素。

在 IE 中只彈出了 "a1" 提示框,即只有字符串 "a1" 中的腳本執行。這是 IE 的一個 Bug,當 SCRIPT 元素為插入字符串的第一個元素時,即使按照 MSDN 所述為 SCRIPT 元素增加了 defer 屬性后,該 SCRIPT 及之后的 SCRIPT 元素內的腳本也無法執行。

所以通常為了使 innerHTML 插入的腳本能夠在 IE 中正常執行,經常會在欲插入的 HTML 代碼字符串的最開始增加一個不可見的元素。如:

<span style="display:none;">span</span><script defer>alert('a1');<\/script>

接下來再看看 Firefox 中使 innerHTML 中的腳本執行的特殊方法:

分析以下代碼:

<html>
<head>
</head>
<body>
<div id="d1"></div>
<script>
    var a1 = "<script>alert('a1');<\/script>";
    var d1 = document.getElementById("d1");
    var sib = d1.nextSibling;
    var pn = d1.parentNode;
    pn.removeChild(d1);
    d1.innerHTML = a1;
    if (sib) {
        pn.insertBefore(d1, sib);
    } else {
        pn.appendChild(d1);
    }
</script>
</body>
</html>

上面代碼中,字符串 "a1" 中包含 SCRIPT 元素,接下來將待插入 HTML 代碼的 DIV 元素【d1】從其父元素中移除,然后通過 innerHTML 將 "a1" 中的 HTML 代碼插入到【d1】中,最后再將【d1】恢復至其原父元素中。

這段代碼在 Firefox 中也成功的使 SCRIPT 中的腳本執行。

解決方案

上面提到的 IE 及 Firefox 中使通過 innerHTML 方法動態插入的 SCRIPT 元素中腳本執行的方法均比較特殊,是利用了瀏覽器的 Bug,或者是利用了瀏覽器提供的特性。而 innerHTML 方法只是用來插入 HTML 代碼,並不能使其中包含的腳本代碼執行。

為了達到最好的兼容性,應避免利用瀏覽器特性及 Bug 使 innerHTML 插入的 SCRIPT 中的代碼執行。所以上述 IE 和 Firefox 中的方法不可行。同時這種做法具有安全隱患。

對於可控來源的動態腳本,使用 createElement 創建 SCRIPT 元素並追加至頁面的文檔樹中,以保證動態腳本的執行。

參見

知識庫
相關問題

測試環境

操作系統版本:
Windows 7 Ultimate build 7600

瀏覽器版本:
IE6
IE7
IE8
Firefox 3.6.3
Chrome 5.0.375.17 dev
Safari 4.0.5
Opera 10.51

測試頁面:
defer1.html
defer2.html

本文更新時間:
2010-08-16

關鍵字

innerHTML 動態 加載 JavaScript SCRIPT defer 腳本

本篇文章正在公測階段,錯誤在所難免,歡迎大家提出寶貴的意見和建議。


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



BX9029: IE 和 Firefox 可以通過特定方法使 innerHTML 方法載入的 SCRIPT 標簽中的 JavaScript 代碼在頁面加載后也可 頁面加載完后自動執行一個方法的js代碼 在頁面加載完后執行javascript代碼 如何實現在頁面完全加載后執行javascript代碼 頁面動態加入
 
粤ICP备14056181号  © 2014-2021 ITdaan.com