jQueryのクラスセレクタは約8~11倍、getElementByIdより遅いことがあるぞ (DOMを10,000回取得しただけ)


DOM取得にどれくらい時間がかかるのか、数値として知りたかったので調べた。

  • 環境
    • MacOS Mojave(2019) Chrome最新
    • ベタ書きのHTMLファイルを用意して、JSもそこに書いちゃう。
    • jQueryは3系
    • VSCode拡張の LiveServer経由で開く。
  • やること
    • DOMを10,000回取得
  • やるやつ
    • Document.getElementById() (<= 一番速そう)
    • Document.getElementsByClassName()
    • jQuery IDセレクタ これ => (“#id”)
    • jQuery IDセレクタ これ => (“.class”) (<= 一番遅そう?) 

1.1. 計測結果

以下みたいな感じになりました。

処理 速度 倍率
getElementById() 1.372 ms x1
getElementsByClassName() 1.374 ms x1.002
jQuery IDセレクタ 5.203 ms x3.792
jQuery クラスセレクタ 12.028 ms x8.767

何回かやったら、だいたい x8~11。複数回やって正確な平均欲しいけれど今日はやらない。意外にもbyClassNameの方がbyIdと同じくらいの速度で嬉しい。

1.2. 計測結果(追記: 2021.03.04)

document.querySelector()メソッドと、Document.querySelectorAll()メソッドの計測が抜けていました(コメントくださった方、ありがとうございます)。そのため、この2つを加えて再計測しました。

処理 速度 倍率 順位(1 ~ 8)
getElementById() 1.297 ms x1.000 1
getElementsByClassName() 1.292 ms x0.996 1
jQuery IDセレクタ 5.075 ms x3.913 7
jQuery クラスセレクタ 11.831 ms x9.122 8
querySelector()_(ID取得) 新規 2.250 ms x1.735 4
querySelector()_(クラス取得) 新規 1.500 ms x1.157 3
querySelectorAll()_(ID取得) 新規 3.929 ms x3.030 6
querySelectorAll()_(クラス取得) 新規 3.740 ms x2.883 5

querySelectorではIDとクラスの両方を取得できるので、両方の結果を書いています。

  • getElementById と getElementsByClassName は同じくらいの速度。今回のように、byClassNameの方が早いことも時々ある。
  • querySelector()メソッドは、クラス指定で取得した方が早い
  • querySelectorAll() においても、クラス指定の方が早い。マッチしたものを全て探すので、当然にquerySelectorより遅い。

1.3. 順番整理(追記: 2021.03.04)

実行にかかった時間の早い順に並べます。

getElementById() <=
getElementsByClassName() <
querySelector()_(クラス取得) <
querySelector()(ID取得) <
querySelectorAll()
(クラス取得) <
querySelectorAll()_(ID取得) <
jQuery IDセレクタ <
jQuery クラスセレクタ


以上です。
以下は余談です。

2. 計測手順

2.1. HTMLを用意

    <!-- jQueryも読んでおく -->
    <div id="hello-id" class="hello-class">hello</div>

2.2. 計測するためのコードを用意

      const DisplayProcessTime = (object) => {
        const N = 10000;
        console.time(object.name);
        for (let i = 0; i < N; i++) {
          object.func();
        }
        console.timeEnd(object.name);
      };

2.3. 計測される側の処理を用意

なんとなく配列でまとめた。

      const domGetters = [
        {
          name: "Document.getElementById()",
          func: () => document.getElementById("hello"),
        },
        {
          name: "Document.getElementsByClassName",
          func: () => document.getElementsByClassName("hello"),
        },
        {
          name: "jQuery_ID",
          func: () => $("#hello"),
        },
        {
          name: "jQuery_class",
          func: () => $(".hello"),
        },
      ];

2.4. 計測する

      for(let i = 0; i < domGetters.length; i++) {
          DisplayProcessTime(domGetters[i]);
      }

2.5. ブラウザコンソールで確認(おわり)

処理 速度 順位
getElementById() 1.3720703125 ms 1
getElementsByClassName() 1.3740234375 ms 2
jQuery IDセレクタ() 5.203857421875 ms 3
jQuery クラスセレクタ 12.028076171875 ms 4
  • Chromeしか計測してない
  • 何回かやったけれど、だいたいこれくらいの感じ
  • これもさらにループして平均出したいけど、今日はやらない

2.6. 再計測 => ブラウザコンソールで確認(追記: 2021.03.04)

再計測を行った時の結果を残しておきます。

処理 速度 順位(1 ~ 8)
getElementById() 1.297119140625 ms 1
getElementsByClassName() 1.2919921875 ms 1
jQuery IDセレクタ() 5.074951171875 ms 7
jQuery クラスセレクタ 11.8310546875 ms 8
querySelector()(ID取得) 2.249755859375 ms 4
querySelector()(クラス取得) 1.4990234375 ms 3
querySelectorAll()(ID取得) 3.92919921875 ms 6
querySelectorAll()(クラス取得) 3.74072265625 ms 5

3. おわりに

DOM取得にどれくらい時間がかかるのか、数値として知りたかったので調べた。
全部の環境でそうとは限らないから、本当のところはよくわからない。

書き方や使いどころを間違えないことは大事。

getElement~ って書くのがめんどい時は、ヘルパー関数的なのをつくればよいかも。

計測にかかった時間よりもこの記事を書く時間の方が多くかかった。

querySelectorを使用した場合の速度は、クラス指定の方がID指定より早かった点が気になる。

返り値の型の使い易さだったり、他のコード群との親和性だったりと、実際のプロダクトでは速度以外に考えるべきことは無数にある。

参考