IE 和 Firefox 能夠經過特定方法使 innerHTML 方法載入的 SCRIPT 標...

IE 和 Firefox 能夠經過特定方法使 innerHTML 方法載入的 SCRIPT 標籤中的 JavaScript 代碼在頁面加載後也能夠執行

標準參考

根據 W3C HTML4.01 規範中的描述,SCRIPT 標籤內的 "腳本" 只會在頁面加載時執行一次,或者經過綁定事件實如今頁面加載後腳本可以重複地執行。 html

defer 屬性是 SCRIPT 元素的特有屬性,這是一個布爾型屬性,它通知用戶端這段腳本中不會生產文檔內容(如 "documnet.write" ),因此沒必要如今當即執行,通常的擁有 defer 屬性的 SCRIPT 元素中的腳本會較晚的被執行。 瀏覽器

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

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

問題描述

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

形成的影響

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

受影響的瀏覽器

IE6 IE7 IE8 使用 innerHTML 方法插入腳本時,SCRIPT 元素必須設置 defer 屬性。
Firefox 先將被插入 HTML 代碼的元素從其父元素中移除,而後使用innerHTML插入包含SCRIPT元素的代碼,最後將這個元素恢復至原父元素中,則通過此操做後SCRIPT中的腳本能夠被執行。

問題分析

全部瀏覽器中,默認狀況下經過 innerHTML 方法動態插入到頁面中的 SCRIPT 標籤中的腳本代碼均不能被執行。 code

分析如下代碼: orm

<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 腳本。 htm

這段代碼在全部瀏覽器中均沒有執行 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 元素並追加至頁面的文檔樹中,以保證動態腳本的執行。

相關文章
相關標籤/搜索