以下斜め読んだ内容

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

Brandon Aaron「jQueryでイベント委譲(Event Delegation)」

Brandon Aaronによる2010.3.4のブログエントリ

Event Delegation with jQuery

  • jQueryでEvent delegationがいかに快適になるかを力説
  • .live()/.die()や.delegate()/.undelegate()の使い所
  • .live()や.delegate()ではダメときはどうする?
    • 答:.bind()とか.closest()とか使うですよ

コンパクトにまとまったエントリ
さらば.live()。今は.delegate()がお洒落」を真に受けてた自分の目を覚ましてくれたよい記事

以下斜め読んだ内容
  • Event delegationの実装はjQueryだと快適にできる
  • jQuery 1.4.2時点では、jQuery使ったevent delegation実装の選択肢は3つ
    • .live()
    • .delegate()
    • 自力でゴリゴリ
  • おさらい:event delegation
    • 発火したイベントを親要素〜祖先要素へと伝播させること(イベントをbubble upさせること)を利用する
    • イベントのbubble upを利用すると、100個のエレメントへイベントハンドラをセットする代わりに1つのエレメントにセットするだけで済ませることができるようになる。
    • 現在DOMツリーにあるエレメントだけでなく、オンザフライで生成・挿入されたエレメントに対しても、クリックしたときにセットしたい処理を自動的に割り当てされる。

例を使ってevent delegationの効用説明

  • テーブルの各行がクリックしたら何かイフェクトを追加したいとする
    • 例えばセルの背景色をハイライト
  • よくある実装は、.bind()の利用
    • このタイプの実装は、セルの数が増えたら(数十、数百)になったらどうする?
      • 各trにイベントがセットされることになる。
      • tr要素の数の増減とかを監視し続けないといけなくなる。そのためのコードも必要
$('tr').bind('click', function(event) {
    // this == tr element
});
  • event delegationを使った実装
    • クリックイベントを各tr要素ではなく親要素であるtableにセット
      • 親・祖先要素だったら何でもいい。body要素でもhtml要素でもwindowオブジェクトでもいい。
    • tr要素で発生したクリックイベントをtable要素までbubble upさせる
    • どのtr要素がクリックされたのかは捕捉される。
  • event delegationが使える理由2つ
    • セットされるハンドラの数を最小化
      • 全てのtr要素にセットから、table要素1つだけにセットへ
    • 動的なデータのハンドリングに向いてる
      • 生成されたtr要素のクリックイベントも「自動的に」捕捉される。

.live()でevent delegation

  • .live()はjQuery1.3で追加
  • event delegationの実装としては一番シンプルにできる
  • .bind()を使った実装は.live()使うとこうなる(↓)
$('tr').live('click', function(event) {
    // this == tr element
});
  • .live()ではdocumentオブジェクトにイベントハンドラがセットされる
    • 1.4では、セットしたい要素/オブジェクトを指定できるようになった。
$('tr',"div#wrapper")//第二引数にコンテキストを指定(空欄だとdocumentオブジェクト=コンテキスト)
    .live('click', function(event) {
    // this == tr element
});
  • コンテキスト?という人は、別エントリ書いた(Understanding the Context in jQuery)から読め
  • .live()の弱点
    • traverse系のメソッドチェーンの中で一緒に使えない
$('#myContainer').children('table').find('tr').live('click', function(event) {
    /*** 動かない ***/
});
  • .live()はメソッドチェーンの一番最初で使わないといけない
  • .die()は、.live()で追加したハンドラを削除するのに使う

.live()でevent delegation

  • .delegate()はjQuery1.4.2で追加
  • .bind()や.live()を使った実装は.delegate()使うとこうなる(↓)
$('table').delegate('tr', 'click', function(event) {
    // this == tr element
});
  • 上記コードではハンドラがセットされてるのは(documentオブジェクトではなく)table要素
  • イベントをbubble upさせる範囲がtable要素に限定されてる
  • .delegate()では、(1)どの要素からdelegateしたいのか、(2)イベント発火をキャッチする範囲の把握がカンタン
  • .live()では無理だったtraverse系のメソッドチェーンの中でも使える(↓)
//これは動く
$(#myContainer').children('table').delegate('tr', 'click', function(event) {
    // this == tr element
});

自力でゴリゴリevent delegation

  • 実装したいevent delegationにとって.live()や.delegate()は自由度低い・制約多いと感じる人向け
  • 例の表のセルへクリックしたらイフェクトでるやつの実装例(↓)
$('table').bind('click', function(event) {
    // this == table element
    var $tr = $(event.target).closest('tr');
});
  • 上でやってること
    • table要素へハンドラセット
    • クリックイベントの発火場所を、event.targetで捕捉
      • 捕捉範囲は、table要素内に限定
    • 発火場所に一番近いtr要素を.closest()で特定
    • 実装のサンプルページ

まとめ(各手法の使い所)

  • .live()使え
    • 手早くevent delegationしたいとき
    • メソッドチェーンできなくてもいいとき
    • ハンドラをセットしたいオブジェクトがdocumentオブジェクトでいいとき
  • .delegate()使え
    • メソッドチェーン使いたいとき
    • .live()よりももう少し自由度高いほうがいいとき
  • 自力でゴリゴリやれ
    • 最大限の自由度と柔軟性を享受しながらevent delegationしたいとき