JavaScript の要素追加・変更で innetHTML と DOM の速度検証

ちょっと昔に散々でまわったネタなんですけど・・・まぁ自分への備忘録っつーことで。

JavaScript でごにょごにょ動的に見た目を変更する社内ツールがあります。自分が作ったヤツなんですが、最近どうにも動きがモッサリしてきました。解析するまでもなく遅い原因は DOM で要素を追加・削除を大量にやっているのはわかっています。だいぶ前に DOM と innerHTML のどっちが高速化ってのが話題になった時期がありましたが、僕の経験(技を何も使わずに単純に DOM 操作を書く場合。というか普通の人の書き方がココに当てはまると思う。)では圧倒的に innerHTML が高速な場合が多いです。

※innerHTML vs DOM ネタはこの辺が参考になります。


さて、話を戻して、じゃぁ作るときに初めっから innerHTML で作れよと言われそうですが、そのときには select / option タグの部分が innerHTML でやると ie ではうまく要素変更ができなかったからでした。急いでいたので DOM 操作で取り急ぎ動くものを作った感じ。そういえば、table 要素も ie はうまく操作できないんですよね。これもはまった。

で今日時間ができたので気になっていた点をちょっと調べてまとめてみました。

- スポンサーリンク -

さて、このエントリを書き進めていくうちに innerHTML に関してはアレコレ検証項目が増えてきたので、その件に関しては別エントリでまとめることにしました。でこのエントリでは結局


  • 普通の技を使わない書き方をしたときには innerHTML がやっぱり便利で高速?
  • 文字列結合する場合は、+= じゃなくて join 使った方が高速

の検証ネタに成り下がりました。では以下検証結果です。


文字列結合する場合は、+= じゃなくて join 使った方が高速

恥ずかしながら、文字列を大量に結合するような処理を += 使って処理していました。配列を用意して最後に join("") した方が ie では10倍高速です。取りあえずソースコードはこんな感じ。縦に長くなってしまうのでソースの主要部分だけ抜粋。詳細はこのエントリのソース見てください。。。どっかで見たようなソースですが大いに参考にさせて頂きました。
var run = {
    TX_PLUS : function(){
        tx_clear(); var html = ''; for (var i = 0; i < count; i++){ html += 'あ'; } $('tx').innerHTML = html;
    },
    TX_JOIN : function(){
        tx_clear(); var html = []; for (var i = 0; i < count; i++) { html.push('あ'); } $('tx').innerHTML = html.join("");
    }
}[button.value];
繰り返し数 


ちなみに僕の環境ではいろいろなブラウザで検証したらこのような結果になりました。

  ie6 firefox2 opera9 safari3 for win
TX_PLUS 1594 ms
0.03188 ms/op
1875 ms
0.0375 ms/op
16 ms
0.00032 ms/op
47 ms
0.00094 ms/op
TX_JOIN 109 ms
0.00218 ms/op
3562 ms
0.07124 ms/op
62 ms
0.00124 ms/op
94 ms
0.00188 ms/op


普通の技を使わない書き方をしたときには innerHTML がやっぱり便利で高速?

ふぅ〜やっとここにたどり着きました。本当はこれが本業の JS 高速化で修正した内容に該当する検証だったのですが・・・。同じようにベンチマークしてみました。弾さんのエントリにあるようなテクを使わない単純な書き方の場合の比較です。ie は select 要素の内容を innerHTML で変更することは出来ません。読み取り専用なので outerHTML を使っています。FireFox では outerHTML は動きませんけどその件はまた別エントリで。

これまた主要部分のみソース抜粋。

var run = {
    S_innerHTML : function(){
        var canvas1 = $('s1');
        var html = [];
        for (var i = 0; i < count; i++){
            if(i == count - 1)
                html.push('<option value="' + i + '" selected="selected">option要素を追加しました - ' + i + '</option>');
            else
                html.push('<option value="' + i + '">option要素を追加しました - ' + i + '</option>');
        }
        canvas1.innerHTML = html.join("");
        canvas1.selectedIndex = count - 1;
    },
    S_outerHTML : function(){
        var canvas1 = $('s1');
        var html = [];
        html.push('<select id="s1">');
        for (var i = 0; i < count; i++){
            if(i == count - 1)
                html.push('<option value="' + i + '" selected="selected">option要素を追加しました - ' + i + '</option>');
            else
                html.push('<option value="' + i + '">option要素を追加しました - ' + i + '</option>');
        }
        html.push('</select>');
        canvas1.outerHTML = html.join("");
        canvas1.selectedIndex = count - 1;
    },
    S_DOM : function(){
        var canvas1 = $('s1');
        for (var i = 0; i < count; i++){ canvas1.options[i] = new Option('option要素を追加しました - ' + i, i); }
        canvas1.selectedIndex = count - 1;
    }
}

select - option における innerHTML(outerHTML) vs DOM のテスト

要素挿入 

ちなみに僕の環境ではいろいろなブラウザで検証したらこのような結果になりました。

  ie6 firefox2 opera9 safari3 for win
innerHTML × 16 ms
0.016 ms/op
16 ms
0.016 ms/op
16 ms
0.016 ms/op
outerHTML 16 ms
0.016 ms/op
× 16 ms
0.016 ms/op
16 ms
0.016 ms/op
DOM 546 ms
0.546 ms/op
63 ms
0.063 ms/op
31 ms
0.031 ms/op
31 ms
0.031 ms/op


ってわけで単純な書き方で超適当な高速化ネタは終了です。取りあえず innerHTML / outerHTML を使っておけばいいんじゃない?ってことで。便利で高速だし。ie or それ以外の判定は

if(document.all) {
    $('id').outerHTML = ....;
} else {
    $('id')1.innerHTML = ....;
}

とでもして処理分岐しておけば取りあえずOKだし。さて、次エントリへ innerHTML / outerHTML ネタは続きます。

- スポンサーリンク -