60

次のようなものを追加しないように言われelement.innerHTML += ...ました:

var str = "<div>hello world</div>";
var elm = document.getElementById("targetID");

elm.innerHTML += str; //not a good idea?

何が問題なのですか? 他にどのような選択肢がありますか?

4

9 に答える 9

59

が設定されるたびinnerHTMLに、HTML を解析し、DOM を構築して、ドキュメントに挿入する必要があります。これには時間がかかります。

たとえば、elm.innerHTML何千もの div、テーブル、リスト、画像などがある場合、呼び出し.innerHTML += ...により、パーサーはそのすべてをもう一度再解析します。これにより、すでに構築された DOM 要素への参照が壊れ、他の混乱が生じる可能性もあります。実際には、最後に 1 つの新しい要素を追加するだけです。

呼び出すだけの方が良いですappendChild

var newElement = document.createElement('div');
newElement.innerHTML = '<div>Hello World!</div>';
elm.appendChild(newElement);​​​​​​​​​​​​​​​​

このようにして、 の既存の内容がelm再度解析されることはありません。

注: [一部の] ブラウザーは、+=オペレーターを最適化し、既存のコンテンツを再解析しないほど十分にスマートである可能性があります。これについては調べていません。

于 2012-07-17T02:52:23.850 に答える
34

はい、elm.innerHTML += str;非常に悪い考えです。完璧な代替品として
使用してください。elm.insertAdjacentHTML( 'beforeend', str )

典型的な「ブラウザはDOMを再構築する必要があります」という答えは、実際には質問の正義を行いません:

  1. 最初に、ブラウザーは elm の下の各要素、それらの各プロパティ、およびそれらのすべてのテキストとコメントとプロセス ノードを調べ、それらをエスケープして文字列を作成する必要があります。

  2. 次に、追加する長い文字列があります。このステップは大丈夫です。

  3. 第 3 に、innerHTML を設定すると、ブラウザーは通過したばかりのすべての要素、プロパティ、およびノー​​ドを削除する必要があります。

  4. 次に、文字列を解析し、破棄したばかりのすべての要素、プロパティ、およびノー​​ドから構築して、ほぼ同一の新しい DOM フラグメントを作成します。

  5. 最後に、新しいノードをアタッチし、ブラウザーは全体をレイアウトする必要があります。これは回避できる可能性があります (以下の代替案を参照)。ただし、追加されたノードにレイアウトが必要な場合でも、古いノードのレイアウト プロパティは、新しいノードから再計算されるのではなく、キャッシュされます。

  6. しかし、それはまだ終わっていません!ブラウザは、すべてのJavaScript 変数をスキャンして古いノードをリサイクルする必要もあります。

問題:

  • 一部のプロパティは HTML に反映されない場合があります。たとえば、現在の値<input>が失われ、HTML の初期値にリセットされます。

  • 古いノードにイベント ハンドラーがある場合、それらは破棄され、すべてを再接続する必要があります。

  • js コードが古いノードを参照している場合、それらは破棄されずに孤立します。それらはドキュメントに属していますが、もはや DOM ツリーにはありません。コードがそれらにアクセスすると、何も起こらないか、エラーがスローされる場合があります。

  • どちらの問題も、js プラグインとの相性が悪いことを意味します。プラグインはハンドラーをアタッチしたり、古いノードへの参照を保持したりして、メモリ リークを引き起こす可能性があります。

  • innerHTML で DOM 操作を行う習慣がつくと、誤ってプロパティを変更したり、望まないことを行ったりする可能性があります。

  • ノードが多いほど、これは非効率的になり、無駄にバッテリー ジュースが増えます。

要するに、それは非効率的で、エラーが発生しやすく、単純に怠惰で情報が不足しています。


最良の代替手段はElement.insertAdjacentHTML、他の回答で言及されていないことです。

elm.insertAdjacentHTML( 'beforeend', str )

innerHTML の問題がない、ほぼ同じコード。再構築、ハンドラーの損失、入力のリセット、メモリの断片化、悪い習慣、手作業による要素の作成と割り当てはありません。

プロパティを含む 1 行の要素に html 文字列を挿入することができ、複合要素や複数の要素を挿入することもできます。その速度は最適化されており、Mozilla のテストでは150倍高速です。

クロスブラウザーではないと言われても、HTML5標準ですべてのブラウザーで利用できるのでとても便利です。

二度と書くなelm.innerHTML+=

于 2015-11-30T09:40:23.173 に答える
7

代替手段は.createElement().textContent、および.appendChild()です。追加+=は、大量のデータを扱っている場合にのみ問題になります。

デモ: http://jsfiddle.net/ThinkingStiff/v6WgG/

脚本

var elm = document.getElementById( 'targetID' ),
    div = document.createElement( 'div' );
div.textContent = 'goodbye world';
elm.appendChild( div );

HTML

<div id="targetID">hello world</div>
于 2012-07-17T02:58:50.110 に答える
2

ユーザーが古いバージョンの IE を使用している場合 (または新しいバージョンも試していない場合)、innerHTML を使用するtdと問題が発生します。IE のテーブル要素は読み取り専用で、ちっちゃいです。

于 2012-07-17T02:54:00.240 に答える
1

マイクの答えはおそらくより良いものですが、別の考慮事項は、文字列を扱っていることです。また、JavaScript での文字列連結は、特に一部の古いブラウザーでは非常に遅くなる可能性があります。HTML の小さな断片を連結しているだけなら、おそらく目立たないでしょうが、ページの大部分に何かを繰り返し追加している場合は、ブラウザで顕著な一時停止が見られる可能性が非常に高くなります。

于 2012-07-17T02:56:25.800 に答える