あまり新しくないもの

新しさはそんなに求めず、自分のすきなことをやりたい人生だった

jQuery の.on()をもういっぺん理解してみる

jQueryのイベントハンドリングには、現在 .on(),.off()のみが推奨されて利用されています。

その基本的な使い方はこんなかんじでした。

$(element).on(event, function() {
    console.log('event fired!');
});

この場合、対象となる$(element)において指定したeventが発火した時に、コールバック関数が呼び出されます。非常に直感的ですね。

ところで、これは以前の.bind()と全く同じです。

.bind()の場合は、指定したずばりその要素に対してのみ、イベントハンドリングを行います。よって、.bind()が呼び出されたその時点で存在しない要素に対しては、たとえセレクタが一致していたとしてもハンドリングが行われません。

このことを利用すれば、イベントハンドリングをしたいまさにその要素にのみ、たとえその後同じセレクタが生成されてこちらにはイベントハンドリングを行いたくない場合でも、イベントハンドリングを行うことが出来ます。

<div class="parent">
<a id="target"></a>
</div>
var $parent = $('.parent'),
    $a = $parent.find('a');
$a.on('click', function() {
    console.log($(this).attr('id'));
});

var $dom = $('<a id="notTarget"></a>');
$parent.append($dom);
// #target をクリックするとイベント発火
// #notTarget をクリックしてもイベント発火しない

delegate使う

delegateってなにかっていうと、親要素に対してイベントハンドリングを一度だけ行って、その子要素を監視する感じのやつです。

言葉で説明してもわかりにくい系の概念なので、コードを書きます。

<div class="parent">
<a id="target"></a>
</div>
var $parent = $('.parent'),
    selector = 'a';

$parent.on('click', selector, function(e) {
    console.log($(e.currentTarget).attr('id'));
});

var $dom = $('<a id="targetToo"></a>');
$parent.append($dom);
// #target をクリックするとイベント発火する
// #targetToo をクリックしてもイベント発火する

はい、これでどっちのa要素をクリックしてもconsole.log流れます。

要は、いつ対象となるセレクタを持ってる要素が追加されようが、イベントハンドリングした親要素の中に入る限りは、いつだってイベント発火でコールバック関数を呼べるようになるんです。

これは特にajaxでバンバン要素を追加したり削除したりするようなwebアプリケーションで効果を発揮して、かなりメモリを節約出来る感じです。あといちいちイベント張らなくていいので、管理がものすごい楽です。コードも綺麗になる気がします。

delegateとlive

ところで.delegate()の他に.live()っていうメソッドも実はあります。ありますが、ほとんどやってることは同じで、親要素が特定のセレクタなのか、documentなのか、というくらいしか違いがない(と僕は認識してる)ので、.on()を使ってる限りは意識する必要ないです。

ただ、on()の場合は書式が統一されていますが、.bind(), .delegate(), .live()はそれぞれ書式が全然違うので、覚えることが増えて面倒なので.on()で統一するのが楽です。というか.on()が公式で推奨してるメソッドなので.on()にしましょう。

.off()

特にナニも言うことはありません。

普通に使って下さい。 うそです。.off()は実は.on()よりもわかりにくいメソッドな気がしているのでき ちんと整理してみます。

まず、すべてのイベントを削除する場合。

$(element).off();

これでこの要素に紐付いたイベントは全て削除されます。全て削除されるので、下手に使うとライブラリ側がハンドリングしたイベントまで削除されたりして、非常に危険なので非推奨です。

$(element).off(event);

これで特定のイベントだけ削除できます。これでも、だいぶ被害の範囲は少なくなりそうですが、まだまだ油断できません。

$(element).off(event + '.namespace');

はい、これで確実に安全です。jQueryのイベントハンドリングでは、各イベントに対してnamespaceを指定できます。こんなかんじに。

$('a').on('click.myEventSpace', function() {
    console.log('fire');
});
$('a').on('click.hisEventSpace', function() {
    console.log('he is Jhon Do');
});

このときa要素をクリックすれば当然二つのログが流れるわけですが、この後更に

$('a').off('click.myEventSpace');

としてやると、それ以後aをクリックしてもhe is Jhon Doとだけ流れて、1つ目のイベントのコールバック関数は実行されなくなります。

これがjQueryがもつイベントのnamespaceです。これを適切に利用できれば、余計なイベントハンドラーを削除しなくてすみそうです。

delegate

delegateでハンドリングしたイベントについて考えましょう。 考えたいですが、ここまでくればとても簡単なのでサクッと書きます。

$(parentElement).off(event, selector);

こうです。要するに.on()の書式はおなじです。

また、delegateしたイベントハンドラーのすべてを削除したい場合は、

$(parentElement).off(event, '**');

とすることもできます。この場合、delegateじゃないイベントについては削除されません。

さらに、.on()のコールバックを設定するときに特定の有名関数を使っていた場合は、その関数名を指定することでその関数だけを削除することが出来ます。

$(parentElement).off(event, selector, functionName);

簡単ですね。


まとめ

.on().off()は古いバージョンのjQueryでいうところの、 .bind().live().delegate() の三つのメソッドを共通の書式で書けるように統合した便利な感じのメソッド。 それぞれの性質を理解した上で使えばより一層便利できれいな感じにコードを書ける。

あとnamespaceは大事。