jQuery1.4aでのlive event/special event

BPStudy#28 : ATNDの発表資料。
スライドなどは用意しておらず、これで発表する。

自己紹介

技術方面でのキーワード
  • jQuery
    • 主にevent周りを追っかけている
  • Mercurial
    • 俺々管理からチーム開発まで、開発をより上手くまわせるバージョン管理の仕方を追求中
  • Struts2
    • 今仕事で使ってる
    • 気に入った
    • 技術者相手では名前で損してる印象
  • Python
    • Be PROUD社員だったりDjango・PyhonハッカソンPython温泉によく参加してたりで誤解されがちだが全然書けない
    • Perlの方がまだ書ける
Be PROUD

今日の本題jQuery1.4

先日jQuery1.4のアルファ版がリリースされた。
jQuery 1.4 Alpha 1 Released | Official jQuery Blog

  • live eventの改良

以下話さないので原文ママ

  • append, prepend, etc. have been heavily optimized.
  • add has been adjusted to always return elements in document order.
  • find, empty, remove, addClass, removeClass, hasClass, attr, and css have been heavily optimized.


今回はlive eventの改良と、リリースノートには載っていない
special eventの改良について話します。

live event

概略
  • 今ある要素に対してではなく、CSSセレクタに対してeventを貼付ける事ができる
  • 対応eventType: click, dblclick, mousedown, mouseup, mousemove, mouseover, mouseout, keydown, keypress, keyup

使い方

通常のevent貼り剥がしの場合

// 貼付ける
$(selecter).bind(eventType,function(ev){});
// 剥がす
$(selecter).unbind(eventType);

こうすると、今、$(selecter)で選択された要素に対して、
eventType(click等)のevent handlerとして、
第2引数の関数を設定したり、解除したり出来る。


live eventの場合はbind/unbindがlive/dieになる。

// 貼付ける
$(selecter).live(eventType,function(ev){});
// 剥がす
$(selecter).die(eventType);
使い道

はてブでこれを実行して2ページ目を追加表示した後、
2ページ目の中のリンクをクリックしてもalertが出る。

jQuery('a').live('click',function(){
  alert('hoge');
  return false;
});
  • 大量の要素にeventを設定
    • パフォーマンスが文字通り桁違い
console.time('select');
jQuery('a'); //1305件くらいある画面での実行例
console.timeEnd('select');
// 5〜10ms
var f = function(){};

console.time('bind');
jQuery('a').bind('click',f);
console.timeEnd('bind');
// 初回実行時1500ms程度
// 2回目以降実行時200ms程度

jQuery('a').unbind('a');

console.time('live');
jQuery('a').live('click',f);
console.timeEnd('live');
// 5〜10ms
void 0;
  • DOMContentLoadedを待たずにeventを設定
    • デモ
      • Firebug等のconsoleを開いた状態で試せる
      • click eventをlive eventとしてa要素に設定
      • bodyの最後の方で意味もなく読み込みが重いJavaScriptをたくさん読み込んで、DOMContentLoadedを遅延させている
      • DOMContentLoadedにclick eventを設定できているのがわかる

live eventの1.4での強化

サポートしているeventTypeの追加

submit,change,mouseenter,mouseleave,focus,blur

contextのサポート

このような書き方で、context配下に限定して、
cssSelectorに合致する要素にeventを貼る事ができるようになった。

jQuery(cssSelector,context).live(eventType,function(ev){});


このコードは.main配下のaタグ全てがclickでalertを出すようにするが、
1.3.2では使えない。

jQuery('a','.main').live('click',function(){
  alert('hoge');
  return false;
});

一つのCSSセレクタにしてしまえば1.3.2でも同じ事が再現出来る。

jQuery('.main a').live('click',function(){
  alert('hoge');
  return false;
});


例えばCSSセレクタではできない or したくないような
ある程度複雑な絞り込みをした後、
それぞれの配下についてlive eventを貼りたいような場合は、
contextのサポートがないと実現出来ない。

(function($){

$('.main ul').filter(function(i){
  return !((i + 1) % 5);
}).each(function(i,n){
  console.info(n);
  $('a',n).live('click',function(ev){
    alert('hoge');
    return false;
  })
});

})(jQuery);
dataのサポート

第2引数にobjectを渡すとeventオブジェクトのdataプロパティから参照出来るようになった。
いまひとつ有効な使いどころが思いつかない。

var data = {message:'hello'};
jQuery('a').live('click',data,function(ev){
  alert(ev.data.message);
  return false;
});

special event

custom eventを定義するための簡易フレームワークのようなもの。
custom eventについては、この辺を参照


使い方

jQuery.event.special.eventType = { setup:function(){},teardown:function(){}};

このような、setup,teardownをキーに関数を値に持つオブジェクトを渡してやればよい。

special eventの歴史

  1. 1.2.2で導入
  2. 1.3 specialAll導入
  3. 1.4a specialAllのspecialへの一本化
1.2.2で導入されたspecial event

l2137-l2157

mouseenter: {
  setup: function() {
    if ( jQuery.browser.msie ) return false;
    jQuery(this).bind("mouseover", jQuery.event.special.mouseenter.handler);
    return true;
  },

  teardown: function() {
    if ( jQuery.browser.msie ) return false;
    jQuery(this).unbind("mouseover", jQuery.event.special.mouseenter.handler);
    return true;
  },
  
  handler: function(event) {
    // If we actually just moused on to a sub-element, ignore it
    if ( withinElement(event, this) ) return true;
    // Execute the right handlers by setting the event type to mouseenter
    arguments[0].type = "mouseenter";
    return jQuery.event.handle.apply(this, arguments);
  }
},

IE以外のブラウザでのmouseover eventに相当するがIEだとmouseenterで、
このコードではIE以外でもmouseenterを使えるようにし、
mouseenterをクロスブラウザ対応させている。
mouseout/mouseleaveについても同様の事をやる事で、
hoverメソッドをシンプルに実装出来るようにしている。
l2233-l2235

hover: function(fnOver, fnOut) {
  return this.bind('mouseenter', fnOver).bind('mouseleave', fnOut);
},

このspecial event1要素×1eventTypeにつき1回のみbindすることができる。

1.3でのspecialAllの導入

jQuery1.3でのspecialAllの唯一の使用箇所がlive eventを定義している箇所である。
jQuery1.3.2 l2777-l2796

specialAll: {
  live: {
    setup: function( selector, namespaces ){
      jQuery.event.add( this, namespaces[0], liveHandler );
    },
    teardown:  function( namespaces ){
      if ( namespaces.length ) {
        var remove = 0, name = RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)");
        
        jQuery.each( (jQuery.data(this, "events").live || {}), function(){
          if ( name.test(this.type) )
            remove++;
        });
        
        if ( remove < 1 )
          jQuery.event.remove( this, namespaces[0], liveHandler );
      }
    }
  }
}

使い方は↓で、special eventととても似ているが

jQuery.event.specialAll.eventType = { setup:function(){},teardown:function(){}};
  • special eventのsetup
    • 1要素×1eventTypeにつき1回のみ実行される
      • 正確にはbindされていない状態での最初のbindのみ。
      • bind→unbind→bindなら2回実行される
    • そのevenTypeのeventを実際にbindする
  • specialAll
    • 1要素×1eventTypeでも何度でも実行される
    • そのevenTypeのeventを実際にbindしない
1.4aでのspecialAllのspecialへの一本化

specialAllのsetup,teardownの上位互換機能として、
specialのadd,removeが使えるようになった。
live eventの定義もspecialのadd,removeにより実現されるようになった。


また、specialAllはlive eventの定義専用という感が強かったが、
specialに統合され、specialのsetup,teardownと併用できるようになったことで、
できることも増えた。
add自体の機能もspecialAllのsetupより強化され、
addの戻り値が関数の時、それをevent handlerとして実際に貼付ける
といった事ができるようになった。
それによってbind時にoptionを渡して、振る舞いを換えるといったことが可能になった。
multiclick event
multiclick event demo

$('#example')
// 3click
.bind('multiclick', { threshold: 3 }, function( event ) {
alert('Clicked three times!');
})
// 5click
.bind('multiclick', { threshold: 5 }, function( event ) {
alert('Clicked 5 times!');
});