#はじめに
とある要素の中をいじりたいという時 innerHTML
で操作することもあるかもしれません。
しかし、実はこれはあまりよろしくない。
#innerHTML
は既存の要素を破壊(?)する
innerHTML
を書き換えるということはつまり、要素の中身をすべて新しいものに書き換えるということです。
既存の要素は、新たな要素として扱われてしまいます。
##サンプル
例えば、以下のようなHTMLとJavaScriptコードがあったとします。
<div id="contentBlock">
<button id="clickButton">Click Here</button>
</div>
var clickButton = document.getElementById('clickButton');
clickButton.addEventListener('click', function () {
alert('Alert!');
});
ボタンをクリックするとアラートが出るだけの簡単なやつです。
では、button要素の前に innerHTML
を使用して文字列を追加してみます。
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コードがあったとします。
<div id="contentBlock">
<button id="clickButton">Click Here</button>
</div>
var clickButton = document.getElementById('clickButton');
clickButton.addEventListener('click', function () {
alert('Alert!');
});
では、button要素の前に insertAdjacentHTML
を使用して文字列を追加してみます。
var contentBlock = document.getElementById('contentBlock');
contentBlock.insertAdjacentHTML('afterbegin', '<b>Test:</b>');
見た目的には innerHTML
の時と変わりませんが、button要素のイベントが残ったままです。
#insertAdjacentHTML
は innerHTML
よりも処理が速い
MDN にも
余分なシリアル化のステップを回避できる分、
innerHTML
への代入による直接的な操作よりもはるかに高速な動作となります。
とあります。
簡単に計測してみます。
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
の方がパフォーマンスはいいです。
document.getElementById('foo').textContent = '';
#おまけ:HTML要素そのものを挿入したいなら insertAdjacentElement
文字列ではなく要素を挿入したいのなら insertAdjacentElement
もありますよというお話。
Element.insertAdjacentElement() - Web API インターフェイス | MDN
第2引数が HTMLElement
になるだけで基本は insertAdjacentHTML
と変わりません。
var
contentBlock = document.getElementById('contentBlock'),
insetElement = document.createElement('b');
insetElement.textContent = 'Test:';
contentBlock.insertAdjacentElement('afterbegin', insetElement);