innerHTML より insertAdjacentHTML を使う

  • 61
    いいね
  • 0
    コメント

はじめに

とある要素の中をいじりたいという時 innerHTML で操作することもあるかもしれません。
しかし、実はこれはあまりよろしくない。

innerHTML は既存の要素を破壊(?)する

innerHTML を書き換えるということはつまり、要素の中身をすべて新しいものに書き換えるということです。
既存の要素は、新たな要素として扱われてしまいます。

サンプル

例えば、以下のようなHTMLとJavaScriptコードがあったとします。

HTML
<div id="contentBlock">
  <button id="clickButton">Click Here</button>
</div>
JavaScript
var clickButton = document.getElementById('clickButton');
clickButton.addEventListener('click', function () {
  alert('Alert!');
});

ボタンをクリックするとアラートが出るだけの簡単なやつです。
では、button要素の前に innerHTML を使用して文字列を追加してみます。

JavaScript
var contentBlock = document.getElementById('contentBlock');
contentBlock.innerHTML = '<b>Test:</b>' + contentBlock.innerHTML;

するとどうでしょう、ボタンをクリックしても反応しなくなりました。

#contentBlock 内のbutton要素は innerHTML によってHTML文字列として取得され、innerHTML によって新たなHTMLとして #contentBlock に挿入されます。
なので、挿入前と挿入後のbutton要素は別物なのです。

insertAdjacentHTML は既存の要素を破壊しない

element.insertAdjacentHTML - Web API インターフェイス | MDN

insertAdjacentHTML は文字列をHTMLとしてパースしたものを指定した箇所に挿入するメソッドです。

構文
element.insertAdjacentHTML(position, text);

position で挿入する箇所を指定、text で挿入するHTML文字列となっています。
position には以下の4つが指定できます。

  • 'beforebegin'
  • 'afterbegin'
  • 'beforeend'
  • 'afterend'

それぞれの位置関係は以下です。

<!-- beforebegin -->
<element>
  <!-- afterbegin -->
  <child>Text</child>
  <!-- beforeend -->
</element>
<!-- afterend -->

サンプル

先ほどと同じように以下のようなHTMLとJavaScriptコードがあったとします。

HTML
<div id="contentBlock">
  <button id="clickButton">Click Here</button>
</div>
JavaScript
var clickButton = document.getElementById('clickButton');
clickButton.addEventListener('click', function () {
  alert('Alert!');
});

では、button要素の前に insertAdjacentHTML を使用して文字列を追加してみます。

JavaScript
var contentBlock = document.getElementById('contentBlock');
contentBlock.insertAdjacentHTML('afterbegin', '<b>Test:</b>');

見た目的には innerHTML の時と変わりませんが、button要素のイベントが残ったままです。

insertAdjacentHTMLinnerHTML よりも処理が速い

MDN にも

余分なシリアル化のステップを回避できる分、 innerHTML への代入による直接的な操作よりもはるかに高速な動作となります。

とあります。

簡単に計測してみます。

JavaScript
function timeTest() {
  var
    div01 = document.createElement('div'),
    div02 = document.createElement('div'),
    i;

  console.time('innerHTML');
  for (i = 0; i < 1000; i++) {
    div01.innerHTML = div01.innerHTML + 'test';
  }
  console.timeEnd('innerHTML');

  console.time('insertAdjacentHTML');
  for (i = 0; i < 1000; i++) {
    div02.insertAdjacentHTML('beforeend', 'test');
  }
  console.timeEnd('insertAdjacentHTML');
}

結果は Chrome 61 では以下でした。(10回実行したうちのスコアがいいもの)

innerHTML: 35.122802734375ms
insertAdjacentHTML: 3.9697265625ms

10倍近い差があるみたいです。

おそらく innerHTML ではDOMツリーの再構築の負担が大きいものと思われます。

おまけ:要素を空っぽにしたいのなら textContent

とある要素の中身を空っぽにしたいのなら innerHTML よりも textContent の方がパフォーマンスはいいです。

JavaScript
document.getElementById('foo').textContent = '';

おまけ:HTML要素そのものを挿入したいなら insertAdjacentElement

文字列ではなく要素を挿入したいのなら insertAdjacentElement もありますよというお話。

Element.insertAdjacentElement() - Web API インターフェイス | MDN

第2引数が HTMLElement になるだけで基本は insertAdjacentHTML と変わりません。

JavaScript
var
  contentBlock = document.getElementById('contentBlock'),
  insetElement = document.createElement('b');

insetElement.textContent = 'Test:';

contentBlock.insertAdjacentElement('afterbegin', insetElement);