頁面動態加入<script>標籤並執行程式碼

呂大豹發表於2015-01-27

  在頁面中動態追加html片段的時候,有時候動態新增的程式碼會含有<script>標籤,比如用了一些模板引擎,或者你的程式碼有些複雜的時候。然而我們用DOM提供的innerHTML方式來新增程式碼的時候,<script>標籤中的程式碼並不能執行,如果有src屬性,指向的外聯檔案也不會被載入,這並不是瀏覽器的bug,因為w3c文件就是這麼規定的。

     那我們有什麼辦法可以恢復追加的<script>標籤的程式碼執行能力呢?
 
重新構造<script>標籤
     這個思路其實非常簡單,用innerHTML新增的<script>無法執行,但是我們script建立節點,並用appendChild追加上去是可以的,所以我們只需要做一下二次工作就可以了,看下面的例子:
     頁面上有個容器:
<div id="cont"></div>
  js程式碼如下:
var html = '<div>html</div><script>alert(1);<\/script>';
var cont = document.getElementById('cont');
cont.innerHTML = html;
var oldScript = cont.getElementsByTagName('script')[0];
cont.removeChild(oldScript);
var newScript = document.createElement('script');
newScript.type = 'text/javascript';
newScript.innerHTML = oldScript.innerHTML;
cont.appendChild(newScript);
  這只是內聯<script>的方法,如果是引用的外部js檔案,那麼我們需要為新建立的script節點指定src屬性。
 
eval大法
     雖然eval因為其安全性不推薦使用,但是在此特殊場景,使用eval確是非常簡單的解決方案,就是把<script>標籤中的程式碼eval一下手動執行,就ok了,看下面程式碼:
var html = '<div>html</div><script>alert(1);<\/script>';
var cont = document.getElementById('cont');
cont.innerHTML = html;
var oldScript = cont.getElementsByTagName('script')[0];
cont.removeChild(oldScript);
var scriptText = oldScript.innerHTML;
eval(scriptText);
  對於內聯的程式碼,簡單而有效,如果是外部js檔案,那麼還是使用上面的方法,為新建立的script節點指定src屬性。
 
document.write大法
     此方法可以在頁面上直接輸出任何html內容,包含<script>標籤的話會立即執行,所以也是一種方案,如下:
var html = '<div>html</div><script>alert(1);<\/script>';
document.write(html);
  程式碼就直接執行了。但是他的缺點是如果程式碼寫在文件底部,輸出的內容會把頁面上的其他內容全部覆蓋,相當於清空了頁面。解決的辦法只有這樣了:
<div id="cont"><script type="text/javascript">document.write(html);</script></div>
  直接把它放在標籤中,就會往這個標籤中輸出東西了。
 
使用jQuery的html()
     上面的方法說來說去,都不如jQuery簡單,因為jQuery的html()方法內部已經做了處理,如果引數中含有<script>標籤,內部會使用eval和建立新節點的方式進行處理,如果是外聯的js檔案,jQuery會發一個同步的ajax請求來獲取程式碼,注意,是同步的!所以不論是內聯的js程式碼還是外聯的js檔案,都可以正常執行,同時在執行完後刪去<script>標籤。所以,使用jQuery你只需要這樣:
var html = '<div>html</div><script>alert(1);<\/script>';
$('#cont').html(html);
  這個alert就妥妥的執行了。到這裡,我真想說:jQuery,你真是俺親孃!

相關文章