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

以下斜め読んだ内容

pseudo translation of useful posts, book reviews, remarks,etc. twitter: feeddict

Brandon Aaron「誤解されてるjQueryの"Context"」

斜め読み

Brandon Aaronの2009.6.24のエントリ

Understanding the Context in jQuery

jQuery1.3時代に書かれた内容だが賞味期限は切れていない

  • Selectors API(ex.$("a"))などで検索範囲を絞り込むために使う第二引数("Context")の誤用が多い
  • 正しく理解しないと、パフォーマンスアップというご利益は得られないとレクチャー
  • コメント欄では、高速なセレクターの使い方がtest方法付きでレクチャー
$("#main","#top");//この書き方は、contextを指定(変更)できていない。
以下斜め読んだ内容
  • context
    • jQueryで要素指定するときのオプションとして提供。
    • これ使うと、要素の検索範囲を制限できる
      • ex. div#naviの中のa要素だけを取得
  • contextはDOMツリーが巨大なページのときは威力発揮する
  • contextの記事は色々書かれてる。パフォーマンス気にするなら使うべき、etc.
    • contextを使うことがパフォーマンス改善になる点は、同意するがサンプルで書かれてるコードがおかしい。

おさらい:context

$('a').context; 
  • contextはjQueryでは、jQueryオブジェクトプロパティとして用意されてた
    • 上記コードではcontextはdocument要素
  • jQueryオブジェクトのデフォルトのコンテキストは、document要素
    • selectors apiはページ全体に実行されるのが、デフォルト

contextを変更する

  • context=特定ノード
    • これがきちんと動くための条件
    • この条件がよく見落とされてる(含過去の自分)
  • contextにセレクターを渡しているケースが本当に多い
    • 動作はするけど、この場合contextは変更されずdocument要素のまま
//誤解してる人の意図に反してcontextはdocument要素のまま
$('a', '#myContainer').context; 
//ちなみに上は内部的には下のように変換
$('#myContainer').find('a');
//
//
//正しいcontext変更方法
//context用にノード取得
var context = $('#myContainer')[0];
//第二引数にcontext渡す
$('a', context).context; //
が返る

jQuery1.3系と.live()

  • .live()
    • 1.3で追加
    • イベントハンドラを必ずdocument要素にバインドする
    • 1.3.3の追加点
      • .live()でもcontext変更できる。バインドする要素をデフォルトのdocument要素から変更できるようになった。
      • contextを正しく理解して.live()使えば、パフォーマンス改善になる

コメント欄

  • ついでに聞くけど的な質問がでても丁寧にリプライしてる
  • Q:contextが誤用されるときの挙動についてどう思う?
  • A:contextにノードを指定しなかったときは、最初にマッチした要素をコンテキストとするように動作を変えたほうがよいかも
//DOM指定の高速な順番はこれ
$('#myContainer').find('a'); //1
$('a', $('#myContainer')[0]);//2 find()使うよりわずかに遅い
$('#myContainer a');//3
  • 上の順番になるわけ
    • jQueryセレクターエンジンにSizzleを使ってるから
      • Sizzleはセレクタの「右側」からノードの指定を行っていく
      • Sizzleの動作はブラウザのCSSセレクタの解釈と同じ
    • 一番重い$('#myContainer a')は、(1)ページ内の全てのa要素を取得し、それから(2)#myContainer内部のa要素だけを抽出、という手順を取る。
    • 一番速い$('#myContainer').find('a')では、(1)#myContainerを取得し、それから(2)#mayContainer内部のa要素だけを取得してる
    • $('a', '#myContainer')が、ちょっと$('#myContainer').find('a')遅いわけ
      • ノードの指定の動作は、$('#myContainer').find('a')と同じ。
      • $('a', '#myContainer')は内部的に$('#myContainer').find('a')へ変換される。この変換コストが少し遅くなる原因
//正しくcontext使ったDOM指定
var context = $('#primary')[0];
 $('a', context);
  • context使うと上の1〜3よりも速くなる
var context = $('#myContainer')[0];
$('a', context);
//上は下と違う。下では、contextにノードではなくjQueryオブジェクト渡されてるため
$('a', $('#myContainer'));
//上は内部的に下に変換
$( $('#myContainer') ).find('a');
  • 要テスト
    • traverseするDOMツリーの規模や複雑さに依存する。
    • どのDOM指定方法が一番早いかは実際にテストした方がいい。
    • getElementsByClassName()実装してるブラウザとそうでないブラウザは、結果が変る可能性あり。
感想
  • contextは確かに誤解されてるみたい。
  • 高速なDOM指定の方法は勉強になった
  • 直感的に書きやすい書き方が一番遅いというのは困った。
  • コメント欄のやり取りまで読むと、高速な書き方は常に決まってるわけじゃない模様
    • DOMツリーの規模やブラウザの実装の影響があるため
    • 使うときはテストして比較したほうがよさそう。