動的に追加される要素にイベントハンドラーをアタッチしたい

例えばテーブルの各行にクリックイベントをアタッチしたいとき、それがAJAXで非同期にデータをロードするテーブルならばロードのたびにアタッチし直さなければならないし、ユーザーが自由に行追加できるテーブルならばその都度追加行にアタッチしなければならない。

動的にイベントをアタッチするようなコードは、同じイベントを二重にアタッチしてしまう、といった事故を起こしがち。できればイベントのアタッチはページロード時に一発で済ませたい。そんなときはdelegatedなイベントハンドラーを利用するとよい。

環境

jQuery 1.7以降

delegatedなイベントハンドラーとは

イベントの発生元に直接ハンドラーをアタッチするのではなく、親要素にアタッチして子から伝播してきたところを捕まえよう、というコンセプトのハンドラー。

やり方

冒頭に書いたテーブルの例だと以下の通り。

$( "#dataTable tbody" ).on( "click", "tr", function() {
    console.log( $( this ).text() );
});

説明

trではなくその親要素、tbodyにイベントをアタッチする。イベントの発生元が第2引数のセレクタtrにマッチしない限りイベントは発火しない。イベントを捕まえるのはtbodyなので、子要素のtrが増えたり書き換わったりしても影響を受けない。

注意

Attaching many delegated event handlers near the top of the document tree can degrade performance.(中略)For best performance, attach delegated events at a document location as close as possible to the target elements. Avoid excessive use of document or document.body for delegated events on large documents.
.on() | jQuery API Documentation

ドキュメントツリーのトップに近い要素にdelegatedなイベントハンドラーを多数アタッチするとパフォーマンスが落ちる可能性がある。documentとかdocument.bodyとかにやたらとアタッチするのは避け、できるだけターゲットに近い要素にアタッチしましょう、とのこと。