読者です 読者をやめる 読者になる 読者になる

console.lealog();

@leader22のWeb系に関する勉強めもブログですのだ

たまに気になるjQueryセレクタの記法

jQueryとSizzleの関係について - console.lealog();

って記事をこないだ書きました。
予想以上にはてブされてて、みんなjQueryのこと大好きなんやなって思いました。

で、そのときにメモってたやつをまとめたら、それなりの長さになったのでついでに。

// これと
$('#hoge', '#fuga');

// これ
$('#fuga').find('#hoge');

どっちがどうでどうなるの?みたいな、たまに気になる記法についてです。

そもそも

こんなもんが何の得になるんじゃーってのはごもっともで。
最初に断っておくと、数あるクソコードのチューニング対象という切り口で見た時、
こんなものの最適化は序の口にすら当たりません。

そもそもDOMなんてキャッシュして使うもので、何回も取得なんてやらないのが鉄則。
で、その数少ないであろう取得時のお作法なので、そこまで役立つことはないと思います。

が、知ってて損はしないのでいちおう。

たまに気になる記法の違いたち

$(selector, context) === $(context).find(selector)

// 実行されるコードはいっしょ
$(selector, context);
$(context).find(selector);

これはソースコードに書いてある通りですね。
contextが指定されてた場合、それでもってfindします。

まあcontextをキャッシュして使いまわせるシーンでは、
後者のようにfindする方が速くて良いです。

$(selector, context) vs $(context).find(selector)

というわけなので、

<div id="parent">
    <span class="child">C1</span>
    <span class="child">C2</span>
</div>

って場合に、

// 既に親がキャッシュされてたとしたら
var $parent = $('#parent');

$('.child'); // ってするのがいいのか
$parent.find('.child'); // ってするのがいいのか

さて、どっちでしょう!


正解は、

  • findしようとする相手がidセレクタで捕まるなら、$('#id') で取るべし(contextはいらない)
  • その他の場合は、検索範囲を絞ることができる後者、findで取るべし

詳しい理由は次へ。

doc.getElementsByClassName vs context.getElementsByClassName

そもそもなDOMのSelectorAPI的な話ですが、コンテキストは指定した方が速い。
対象がidの場合は、そもそもdocument.getElementByIdであって、context.getElementByIdではない(ってかできない)。

原則DOMはキャッシュして欲しいので、出来る限りcontextを囲い込むべしってことですね。
もちろんTPOはありますけど!

$('.hoge') vs $('span.hoge')

// <span class="hoge">これを取る場合に</span>

// こうするか
$('.hoge');
// こうしたほうがいいのか
$('span.hoge');

これよく見ますねー。
なんか厳密に指定したほうが速くなるような気が・・大きなお世話です!

実装として、

  • 単一のidでもclassでもtagとも判定できない文字列は、もれなくquerySelectorAllされる
  • よって、getElementsByClassNameで済んだセレクタなのに、querySelectorAllされちゃう

よって、前者のが速い。

$('.hoge #fuga')みたいなやつも遅くなるだけなのでやめてあげてください。

$('#hoge') vs $('#hoge', '#hoge-parent')

contextを指定しちゃうとfindになっちゃうので、前者のが速い。

  • 前者はjQueryのスコープでgetElementByIdしてくれる
  • 後者はSizzle($.findの後)でgetElementByIdしてくれる

ちょっとメソッドコールの数が減るくらいですけど、いちおう。

あなたがアレコレ考えなくてもjQuery様がよしなにしてくれるのです!

最後に言い訳

つらつらと書いてきましたがこの記事は、

  • querySelectorAllよりもgetElementsByClassNameの方が速い
  • メソッドコールが少ない = 速い

といったニュアンスで書いてるので、これが当てはまらない環境では相容れない知識です。
まあIE6とか7とか相手にする時代ならともかく、今はだいたいこの認識で間違いないような?と思ってますが。
少なくとも手元のiPhone4/5/6では、圧倒的にquerySelectorAllが遅かったし・・。

ちなみに、抑えておくべき情報は2011年に全て書いてありました。

jQueryのCSSセレクタAPIを高速に扱う方法 | tech.kayac.com - KAYAC engineers' blog

実は公式にも似たような情報がありました・・!

Optimize Selectors | jQuery Learning Center