jQuery の :hidden セレクターは、遅い?

jQuery の :hidden セレクターは遅いのでしょうか?
Excelインクリメンタルサーチのプロファイルをとっていたら、処理回数が多いと無視できないぐらい遅くなることがわかったのですが…。

でも、理由がよくわからないです。

解析

まず、該当箇所(hidden セレクター)のソースを確認してみました。

jQuery.expr.filters.hidden = function( elem ) {
    var width = elem.offsetWidth,
        height = elem.offsetHeight;

    return (width === 0 && height === 0) || (!jQuery.support.reliableHiddenOffsets && (elem.style.display || jQuery.css( elem, "display" )) === "none");
};


どうやら、非表示時には offsetWidth, offsetHeight が両方 0 になることを利用して判定をしているようです*1
jQuery.support.reliableHiddenOffsets 以降は、IE向けの処理なので、今回は関係なさそうです)
たしかに、これならスタイルシートをチェックするよりも速そうです。
でも、遅いんですよね…。

さらに解析

より確実なデータになるよう、console.time() を使ってきっちり時間を図るように改造してみました。

console.time('selector');
var x = jq_results.not(':hidden');
console.timeEnd('selector');


その結果わかったこと。
必ず遅いのではなく、表示位置が変わった後だと遅くなるようです。

  • 初回は遅い(700〜800ms ぐらい)
  • キーワードを消去する(全件表示にする)と、遅い(700〜800ms ぐらい)
  • 表示内容が変わらないと、速い(60〜70ms ぐらい)*2


どうやら、offsetWidth/offsetHeight を事前に計算しておくのではなく、アクセスがあったときに初めて計算しているようです。だから初回が遅いのだと思います。
表示内容が変わらないときに速いのは、その計算結果を使いまわしているからで、位置が変わっていると遅いのは、再計算しているからのようです。


ただ、遅くなるといっても、今回はテスト用に1800行ものデータを使っているから顕著になるだけで、1回あたりの時間は 2.25〜2.5ms。
ふつうに使う分には、全然気にしなくて大丈夫そうです。


でも、コンソールで動かすと速い

念のため、検証です。
ノードを非表示にした後に hidden セレクターで要素を取得するようなコードを、デベロッパーツールのコンソールで動かしてみると…。

…あれ?
これだと 29ms しかかかっていません(Firefoxでも60ms程度)。
(プロファイルを取ってみましたが、上記と同じ処理が走っていることは間違いありませんでした)


真ん中の3行(hidden セレクターをの部分)だけを実行すると、約7msと速くなります。
なので、上記の予想は当たっているみたいですが、Excelインクリメンタルサーチで極端に遅くなる理由がよくわからないです…。

それでもバージョンアップ

理由はよくわからずじまいでしたが、Excelインクリメンタルサーチで hidden セレクターがネックになっているのは間違いないようです。
なので、その hidden セレクターを使わないで、自分で要素を取得するように修正しました*3


他にもいくつか改良して、最終的には4倍ぐらい速く、快適に使えるようになりました。
ご利用中の方はぜひ、最新バージョン(ver1.1)をご利用ください!

Excelインクリメンタルサーチ
Excelの表を、すごく検索しやすくするツールです。
(QA表, 不具合表... etc)

サンプルはこちら!


このツールを使うと、Excelの表をブラウザ上で素早く、わかりやすく検索できます。
データの自動変換スクリプトがついているので手間はほとんどかかりません。
また、複数のファイルにまたがるような場合でも、簡単に検索できるようになります。


日頃、Excelでバリバリ仕事をこなさなきゃいけないSIerの方にうってつけのツールです。
ぜひ、ご利用ください!


ダウンロード:ダウンロード - 地平線に行く
紹介ページ:Excelインクリメンタルサーチ - 地平線に行く


ver1.1 での変更点

  • 機能追加
    • 検索キーワードをURLのハッシュ(#〜)で指定できるようにした
    • 消去ボタンを削除し、キーワード上に×ボタンを配置した
  • リファクタリング
    • 検索処理を4倍ぐらい高速化

*1:jQuery 1.3.2 から、とのこと。jQuery1.3.2 リリースノート - jQuery 日本語リファレンス

*2:そもそも、表示内容が変わらないのに書き換え処理が走るのはバグですね(汗)(公開中のバージョンでは修正済みです)

*3:riklomas 氏が公開しているライブラリ(QuickSearch)内なので、フィードバックを贈ろうかとも思ったんですが、原因が不明なのと普通はここまで大きなデータを使わないだろうということで、見送りました。