jQueryの強力な機能のひとつに、 $('#the-target') のようにCSSのセレクターを用いて要素検索できるというものがあります。この要素検索を行うモジュールにはSizzleという名前が付けられています。

一方、近代的なブラウザーには .querySelectorAll() がネイティブで実装されています。こちらもCSSのセレクターで要素検索できるメソッドです。

var $elems = $('elem#id.class, .and.more.selectors');
var elems = document.querySelectorAll('elem#id.class, .and.more.selectors');

JavaScriptで何かするよりも、ネイティブでやった方が高速です。じゃあSizzleなんていらないんじゃないの?と思われるかもしれません。実際、jQuery互換軽量フレームワークを詠うZepto.jsでは、ほとんどそのまま querySelectorAll() を利用しています。(そのために古いIEでは正常に動作しませんが。)

それでもjQueryはSizzleを使い続ける方針です。Sizzleが .querySelectorAll() に勝る点とは、何でしょうか。

参考

クロスブラウザーとバグ対策

まずはこれです。IE 6, 7では .querySelectorAll() が実装されていないので、その場合はもちろんSizzleのようなエンジンが必要になります。

しかしIEに限らず、他のブラウザーもちょっとずつ不具合を抱えている——とjQueryチームは主張しています。Sizzleはそれを吸収する役割を担っているわけです。

昔は .querySelectorAll() はなかった

jQueryが公開された当初は .querySelectorAll() はまだ実装されていませんでした。

Selectors API が使えるブラウザ

2008 年 3 月 6 日現在の一覧

  • WebKit Nightly Builds (開発版の Safari)
  • Internet Explorer – Web Browser for Microsoft Windows (IE8 の Beta 版)

最初のjQuery 1.0が公開されたのは2006年8月26日です。

August 26th, 2006
First stable version of jQuery: jQuery 1.0

なお .querySelectorAll() の実装は先になりましたが、仕様 (Selectors API) 自体は2006年に既に公開されています。

W3C Working Draft 25 May 2006

またjQuery自体も、他のCSSセレクターで要素を検索するJavaScriptライブラリーを参考にしていたようです。

セレクターの拡張

基本的にはW3Cのセレクターの仕様に則っているのですが、敢えて独自仕様に変え、使いやすくしているものもあります。

拡張 :not()

例えば否定疑似クラス :not() は、本来CSSでは単純セレクター (simple selector) を与える事しかできません。Sizzleはこれを拡張し、複合セレクター (complex selector) を利用できるようにしています。

".aaa:not(.xxx)"        // jQuery ... OK, querySelectorAll() ... OK
".aaa:not(.xxx.yyy)"    // jQuery ... OK, querySelectorAll() ... NG

独自セレクター

:first あたりがよく利用されるかなと思うのですが、これはjQueryが独自に制定したセレクターです。(CSSにも :first-child がありますが、別の機能です。) もっとも.querySelectorAll() の方は単純に [0] と取れば済む話ですが……。

jQueryが拡張しているセレクターの一覧はこちら。

  • [jQuery Extensions – jQuery API)[http://api.jquery.com/category/selectors/jquery-selector-extensions/)

幾つか例を挙げます。

:odd, :even

偶数番目、奇数番目のものだけを抽出します。

[attr!=value]

属性の値が指定のものでない要素を抽出します。属性セレクターは = のあたりが何パターンかありますが、 != はCSS2, CSS3では存在しません。

任意の拡張

カスタムセレクターを独自に定義する事ができます。

この例だと :unkomorimori という疑似クラスを定義しています。ウンコー ヽ(・∀・)ノ●

Sizzleを削るとどれくらい軽くなるか

試しに削って圧縮してみました。

状態 ファイルサイズ 比率
元の jquery-1.8.1.js 218,346 100%
そのまま圧縮 93,039 43%
Sizzleを削って圧縮 77,678 36%

15KB軽くなりましたね。大きいような、小さいような。

実際はSizzleの機能に依存している部分があるはず(未確認)ですし、 .querySelectorAll() に置き換えてすらいないで、この通りにはいきません。

圧縮にはUglifyJSを使っています。削ったのはSizzleのコメントが付けられた匿名関数全体です。

Sizzleは遅いか

もちろん .querySelectorAll() と比べると遅いはずですが、そこはあまり気にする必要はないと思います。0.1msと0.01msは10倍の差ですが、体感に差は全くないでしょう?

有為な差が出る程に要素検索を多用しているのなら、それは既に設計の問題です。適宜DOMオブジェクトへの参照をキャッシュしておくべきです。また検索も複雑なセレクターではなくID、せめてクラスを使えば高速化できるでしょう。

で、Sizzleは必要か?

ここまでSizzleの独自機能を挙げてきましたが、それでも .querySelectorAll() が使えるなら、Sizzleはなくても良いんじゃないかなーと正直思ってます……。

ええと、書いてて飽きたのでここまでにします。コメント欄にツッコミお願いしますね。

とっぴんぱらりのぷう。