注:以下はJulien Lecomte の記事「The Problem With innerHTML」の翻訳です。著者は、現代のブラウザーでinnerHTMLメソッドを使用する際の問題を検討し、それを回避するためのヒントを提供しています。 イタリック体での私のコメントinnerHTML
プロパティは、特定のタグのHTMLコンテンツを置き換えることができる完全に基本的なものであるため、そのシンプルさと便利さからWeb開発者の間で非常に人気があります。 DOM Level 2 API(
removeChild
、
createElement
、
appendChild
)を使用することもでき
appendChild
が、
innerHTML
を使用すると、DOMツリーを変更するためのはるかに簡単で効率的な方法です。 ただし、
innerHTML
を使用する場合は、回避する必要があるいくつかの問題があります。
innerHTML
プロパティの不適切な処理は、HTML文字列に遅延としてマークされた<script>
呼び出しが含まれている場合、Internet Explorerでスクリプトインジェクション( XSS )攻撃につながる可能性があります: <script defer>...</script>
innerHTML
プロパティを設定すると、すべてのイベントハンドラーを含む現在のネストされたHTML要素がすべて破棄され、一部のブラウザーでメモリリークが発生する可能性があります。
言及する価値があるいくつかのマイナーな欠陥があります:
- 作成した要素へのリンクを取得することはできません;コードを追加して、それらへのリンクを手動で取得する必要があります(DOM APIを使用)。
- すべてのブラウザーのすべてのHTML要素に
innerHTML
を設定することはできません(たとえば、Internet Explorerでは、表の行( tr )にinnerHTML
を設定できません)。
個人的には、
innerHTML
プロパティの使用に関連するセキュリティおよびメモリ使用量の問題を心配しています。 しかし、説明されている問題は決して新しいものではなく、すでにいくつかの明るい頭脳がそれに注意を払い、それを解決する方法を提案しました。
Douglas Crockfordは
、イベントハンドラーをHTML要素に追加することで発生するいくつかの
循環参照を削除
するpurge
関数を作成し、
ガベージコレクターが関連するすべてのメモリ
を完全に解放できるようにしました。
HTML文字列から
<script>
削除することは、一見したほど簡単ではありません。 この目的に使用される正規表現は非常に複雑でなければなりませんが、考えられるすべてのケースをカバーするかどうかは私にはわかりません。 以下は、仕事で個人的に使用するオプションです。
/ <script [^>] *> [\ S \ s] *?<\ / script [^>] *> / ig
これらの両方の手法を1つの
setInnerHTML
関数に結合してみましょう(
UPD:コメントを追加したすべての人に感謝します:あなたが指摘したすべてのエラー/ホールを修正しました。また、
setInnerHTML
関数を
YAHOO.util.Dom
に含めることにし
YAHOO.util.Dom
)
YAHOO.util.Dom.setInnerHTML = function(el、html){
el = YAHOO.util.Dom.get(el);
if(!el || typeof html!== 'string'){
nullを返します。
}
//循環参照を削除します
(関数(o){
var a = o.attributes、i、l、n、c;
if(a){
l = a.length;
for(i = 0; i <l; 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 <l; i + = 1){
c = o.childNodes [i];
//子ノードを削除します
arguments.callee(c);
//すべてのイベントハンドラーを削除し、
// YUI addListenerを介して要素に追加
YAHOO.util.Event.purgeElement(c);
}
}
})(el);
// HTML文字列からすべてのスクリプトを削除し、innerHTMLプロパティを設定します
el.innerHTML = html.replace(/ <script [^>] *> [\ S \ s] *?<\ / script [^>] *> / ig、 "");
//最初の子ノードへのリンクを返します
return el.firstChild;
};
出来上がり! この関数の実装で何かを見逃した場合や、正規表現を改善する必要がある場合は教えてください。
UPD: Webページに悪意のあるコードを挿入する方法は数多くあります。
setInnerHTML
関数は、すべてのクラスAブラウザー(
Aグレードブラウザー )で実行された場合にのみ
<script>
の動作を正規化し
ます 。 信頼できないHTMLコードを埋め込む場合は、サーバー側で最初にクリアします。 これには膨大な数のライブラリがあります。
メモを読んでくれたみんなに感謝します。 与えられた例を改善する方法についてのあなたのコメントを歓迎します。