web-dev-qa-db-ja.com

「element.innerHTML + =」のコードが悪いのはなぜですか?

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

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

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

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

44
ajax333221

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の既存のコンテンツは再度解析されません。

注: [一部の]ブラウザは、+=演算子。既存のコンテンツを再解析しません。私はこれを研究していません。

35

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

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

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

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

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

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

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

  6. しかし、まだ完了していません!ブラウザは、alljavascript変数をスキャンして古いノードをリサイクルする必要もあります。

問題点:

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

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

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

  • どちらの問題も、jsプラグインには不向きであることを意味します。プラグインはハンドラーをアタッチしたり、古いノードを記憶したり、メモリリークを引き起こす可能性があります。

  • InnerHTMLを使用してDOM操作を行う習慣を身に付けた場合、誤ってプロパティを変更したり、望まない他の操作を行ったりする可能性があります。

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

要するに、それは非効率的であり、エラーを起こしやすく、単に怠け者であり、情報がありません。


最良の代替案は _Element.insertAdjacentHTML_であり、他の回答では言及されていません:

Elm.insertAdjacentHTML( 'beforeend', str )

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

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

誰かがそれがクロスブラウザではないと言った場合、HTML5 標準 であり、 すべてのブラウザ で利用できるのでとても便利です。

_Elm.innerHTML+=_を二度と書かないでください。

21
Sheepy

代替は.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>
_
4
ThinkingStiff

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

1
Tim

InnerHTML chromeはonclickイベントを失います jsFiddle

var blah = document.getElementById('blah');
var div = document.createElement('button');
div.style['background-color'] = 'black';
div.style.padding = '20px;';
div.style.innerHTML = 'a';
div.onclick = () => { alert('wtf');};

blah.appendChild(div);

// Uncomment this to make onclick stop working
blah.innerHTML += ' this is the culprit';

<div id="blah">
</div>
1
Adam Sparks

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

0
jjathman

ショート

_innerHTML += ..._(コンテンツの更新)を_innerHTML = ..._(コンテンツの再生成)に変更すると、非常に高速なコードが得られます。 _+=_の最も遅い部分がDOMコンテンツを文字列として読み込んでいるように見えます(文字列をDOMに変換しません)

innerHTMLを使用することの欠点は、古いコンテンツイベントハンドラーを失うことです。ただし、タグ引数を使用してこれを省略することができます。 <div onclick="yourfunc(event)">は、小規模プロジェクトでは acceptable です

長いです

パフォーマンステストを行いました [〜#〜] here [〜#〜] Chrome、Firefox、Safariで(2019 5月)(マシンで実行できますが、我慢してください-5分ほどかかります)

enter image description here

_function up() {
  var container = document.createElement('div');
  container.id = 'container';
  container.innerHTML = "<p>Init <span>!!!</span></p>"
  document.body.appendChild(container);
}

function down() {
  container.remove()
}

up();

// innerHTML+=
container.innerHTML += "<p>Just first <span>text</span> here</p>";
container.innerHTML += "<p>Just second <span>text</span> here</p>";
container.innerHTML += "<p>Just third <span>text</span> here</p>";

down();up();

// innerHTML += str
var s='';
s += "<p>Just first <span>text</span> here</p>";
s += "<p>Just second <span>text</span> here</p>";
s += "<p>Just third <span>text</span> here</p>";
container.innerHTML += s;

down();up();

// innerHTML = innerHTML+str
var s=container.innerHTML+'';
s += "<p>Just first <span>text</span> here</p>";
s += "<p>Just second <span>text</span> here</p>";
s += "<p>Just third <span>text</span> here</p>";
container.innerHTML = s;

down();up();

// innerHTML = str
var s="<p>Init <span>!!!</span></p>";
s += "<p>Just first <span>text</span> here</p>";
s += "<p>Just second <span>text</span> here</p>";
s += "<p>Just third <span>text</span> here</p>";
container.innerHTML = s;

down();up();

// insertAdjacentHTML str
var s='';
s += "<p>Just first <span>text</span> here</p>";
s += "<p>Just second <span>text</span> here</p>";
s += "<p>Just third <span>text</span> here</p>";
container.insertAdjacentHTML("beforeend",s);

down();up();

// insertAdjacentHTML
container.insertAdjacentHTML("beforeend","<p>Just first <span>text</span> here</p>");
container.insertAdjacentHTML("beforeend","<p>Just second <span>text</span> here</p>");
container.insertAdjacentHTML("beforeend","<p>Just third <span>text</span> here</p>");

down();up();

// appendChild
var p1 = document.createElement('p');
p1.innerHTML = 'Just first <span>text</span> here';
var p2 = document.createElement('p');
p2.innerHTML = 'Just second <span>text</span> here';
var p3 = document.createElement('p');
p3.innerHTML = 'Just third <span>text</span> here';
container.appendChild(p1);
container.appendChild(p2);
container.appendChild(p3);_
_b {color: red}_
_<b>This snippet NOT test anythig - only presents code used in tests</b>_

  • すべてのブラウザで、_innerHTML +=_が最も遅いソリューションでした。
  • すべてのブラウザの最速のソリューションは_innerHTML = str_および _insertAdjacentHTML str_ (最速)でした
  • ケース_innerHTML = innerHTML +str_を詳しく見て、_innerHTML = str_と比較すると、_innerHTML +=_の最も遅い部分は[〜#〜] reading [〜#〜]文字列としてのDOMコンテンツ(文字列をDOMに変換しない)
  • createElement/appendChildは中速ソリューションです
  • DOMツリーを変更する場合は、最初に文字列全体を(htmlを使用して)生成し、DOMのみを更新/再生成します[〜#〜] once [〜#〜]
0