innerHTML 屬性的使用非常流行,因為他提供了簡單的方法完全替代一個 HTML 元素的內容。另外一個方法是使用 DOM Level 2 API(removeChild, createElement, appendChild)。但很顯然,使用 innerHTML 修改 DOM tree 是非常容易且有效的方法。然而,你需要知道 innerHTML 有一些自身的問題: 當 HTML 字符串包含一個標記為 defer 的 script 標簽( script defer /script )時,如 innerHTML 屬性處理不當,在 Internet Explorer 上會引起腳本注入攻擊。 設置 innerHTML 將會破壞現有的已注冊了事件處理函數的 HTML 元素,會在某些瀏覽器上引起內存泄露的潛在危險。 你不能得到剛剛創建的元素的引用,需要你手動添加代碼才能取得那些引用(使用 DOM APIs)。 你不能在所有瀏覽器的所有 HTML 元素上設置 innerHTML 屬性(比如,Internet Explorer 不允許你在表格的行元素上設置innerHTML 屬性)。
我更關注與使用 innerHTML 屬性相關的安全和內存問題。很顯然,這不是新問題,已經有能人圍繞這些中的某些問題想出了方法。 Douglas Crockford 寫了一個 清除函數 ,該函數負責中止由于 HTML 元素注冊事件處理函數引起的一些循環引用,并允許垃圾回收器(garbage collector)釋放與這些 HTML 元素關聯的內存。 從 HTML 字符串中移除 script 標簽并不像看上去那么容易。一個html' target='_blank'>正則表達式可以達到預期效果,雖然很難知道是否覆蓋了所有的可能性。這里是我的解決方案: / script[^ ]* [/S/s]*? //script[^ ]* /ig 現在,讓我們將這兩種技術結合在到一個單獨的 setInnerHTML 函數中,并將 setInnerHTML 函數綁定到 YUI 的 YAHOO.util.Dom 上:
YAHOO.util.Dom.setInnerHTML = function (el, html) { el = YAHOO.util.Dom.get(el); if (!el || typeof html !== 'string') { return null; } // 中止循環引用 (function (o) { var a = o.attributes, i, l, n, c; if (a) { l = a.length; for (i = 0; i i = 1) { n = a[i].name; if (typeof o[n] === 'function') { o[n] = null; } } } a = o.childNodes; if (a) { l = a.length; for (i = 0; i i = 1) { c = o.childNodes[i]; // 清除子節點 arguments.callee(c); // 移除所有通過YUI的addListener注冊到元素上所有監聽程序 YAHOO.util.Event.purgeElement(c); } } })(el); // 從HTML字符串中移除script,并設置innerHTML屬性 el.innerHTML = html.replace(/ script[^ ]* [/S/s]*? //script[^ ]* /ig, // 返回第一個子節點的引用 return el.firstChild; };
很明顯,在網頁上還有很多其他注入惡意代碼的方法。setInnerHTML 函數僅能在所有 A-grade 瀏覽器上規格化 script 標簽的執行行為。如果你準備注入不能信任的 HTML 代碼,務必首先在服務器端過濾,已有許多庫可以做到這點。 原文:Julien Lecomte 的 《The Problem With innerHTML》html教程