gihyo.jp » DEVELOPER STAGE » 特集 » jquery.jsを読み解く » 第8回 jQueryライブラリ(1795行目~1961行目)

jquery.jsを読み解く

第8回 jQueryライブラリ(1795行目~1961行目)

今回は,jQuery.eventに関する処理の説明になります。ここしばらくは,ブラウザの差異を吸収するコードが続いていましたが,今回のエレメントに対してイベントを割り当てる処理の部分も良くできていて,参考になるような書き方がたくさん出てきます。

jQuery.event.add()

1795: /*
1796:  * A number of helper functions used for managing events.
1797:  * Many of the ideas behind this code orignated from 
1798:  * Dean Edwards' addEvent library.
1799:  */
1800: jQuery.event = {
1801:
1802:  // Bind an event to an element
1803:  // Original by Dean Edwards
1804:  add: function(elem, types, handler, data) {
1805:    if ( elem.nodeType == 3 || elem.nodeType == 8 )
1806:      return;
1807:
1808:    // For whatever reason, IE has trouble passing the window object
1809:    // around, causing it to be cloned in the process
1810:    if ( jQuery.browser.msie && elem.setInterval != undefined )
1811:      elem = window;
1812:
1813:    // Make sure that the function being executed has a unique ID
1814:    if ( !handler.guid )
1815:      handler.guid = this.guid++;
1816:      
1817:    // if data is passed, bind to handler 
1818:    if( data != undefined ) { 
1819:      // Create temporary function pointer to original handler 
1820:      var fn = handler; 
1821:
1822:      // Create unique handler function, wrapped around original handler 
1823:      handler = function() { 
1824:        // Pass arguments and context to original handler 
1825:        return fn.apply(this, arguments); 
1826:      };
1827:
1828:      // Store data in unique handler 
1829:      handler.data = data;
1830:
1831:      // Set the guid of unique handler to the same of original handler, so it can be removed 
1832:      handler.guid = fn.guid;
1833:    }
1834:

それでは,1795行目のコメント文から見ていきましょう。Dean Edwards氏のaddEventライブラリから多くのアイデアをもらっていると書かれています。IE7.jsや/packer/を公開されている方で,Dean Edwards: addEvent() - My Solutionに元になっているソースがあるので,興味がある方はこちらも合わせて読んでみてください。

1804行目からのjQuery.event.add()メソッドは,eventtype(),bind(),one() および bindReady()から呼び出され,実際にイベントを割り当てる内部処理用のメソッドです。1805行目で,引数elemに渡された要素のnodeTypeがテキストノードまたはコメントノードの場合には何もせずにreturnしています。

1810行目では,ブラウザがInternet Explorerの場合に発生する不具合対策のためにwindowオブジェクトのクローンを生成しています。

1814行目は,イベントハンドラに対してユニークなIDを生成しています。ここでいうhandlerというのは,実際に行われる処理を定義した関数のことです。

1818行目からは,引数dataが定義されている場合の処理で,イベントハンドラに対して引数dataをバインド(束縛)します。1823行目でhandlerをラップする関数を作成し,元々のhandlerに引数を渡すように再定義しています。これは,イベントの定義時ではなく,実行時のパラメータをhandlerに対してbindするためです。また,handler.dataにdataをhandler.guidに元のguidをそれぞれ設定します。

jQuery.handle

1835:    // Init the element's event structure
1836:    var events = jQuery.data(elem, "events") || jQuery.data(elem, "events", {}),
1837:      handle = jQuery.data(elem, "handle") || jQuery.data(elem, "handle", function(){
1838:        // returned undefined or false
1839:        var val;
1840:
1841:        // Handle the second event of a trigger and when
1842:        // an event is called after a page has unloaded
1843:        if ( typeof jQuery == "undefined" || jQuery.event.triggered )
1844:          return val;
1845:    
1846:        val = jQuery.event.handle.apply(arguments.callee.elem, arguments);
1847:    
1848:        return val;
1849:      });

1836行目は,events変数の定義です。既にeventsが定義されていればその値を取得,なければ初期化します。

1837行目も同様で,既にhandleが定義されていればその値を取得,なければ新たに定義します。関数の中身は,1838~1848行目にて定義されています。1839行目でundefinedやfalseを返すために,戻り値用のval変数を定義します。そして,ページのアンロード時などjQuery変数が未定義の場合やネイティブイベントが実行中でjQuery.event.triggeredがtrueの場合には,undefinedを返します。

そうでなければ,applyメソッドを利用して関数を実行します。arguments.calleeは自身を指すので,arguments.callee.elemが実行時のthisになります。

1850: // Add elem as a property of the handle function 1851: // This is to prevent a memory leak with non-native 1852: // event in IE. 1853: handle.elem = elem; 1854:

1853行目は,handleのプロパティにelemを代入しています。コメントによると,Internet Explorerでのメモリリークを防ぐためのようです。

1855:      // Handle multiple events seperated by a space
1856:      // jQuery(...).bind("mouseover mouseout", fn);
1857:      jQuery.each(types.split(/\s+/), function(index, type) {
1858:        // Namespaced event handlers
1859:        var parts = type.split(".");
1860:        type = parts[0];
1861:        handler.type = parts[1];
1862:
1863:        // Get the current list of functions bound to this event
1864:        var handlers = events[type];
1865:
1866:        // Init the event handler queue
1867:        if (!handlers) {
1868:          handlers = events[type] = {};
1869:    
1870:          // Check for a special event handler
1871:          // Only use addEventListener/attachEvent if the special
1872:          // events handler returns false
1873:          if ( !jQuery.event.special[type] || jQuery.event.special[type].setup.call(elem) === false ) {
1874:            // Bind the global event handler to the element
1875:            if (elem.addEventListener)
1876:              elem.addEventListener(type, handle, false);
1877:            else if (elem.attachEvent)
1878:              elem.attachEvent("on" + type, handle);
1879:          }
1880:        }
1881:

1857行目からは,イベントハンドラを登録する処理で,jQuery(...).bind("mouseover mouseout", fn); のようにイベント名をスペースで区切って,複数のイベントにハンドラを一括登録できます。処理としては,引数をスペースでsplitしてeach()でループしています。

1859行目は,引数type('click'などが入ります)を'.'で分割しています。これはどういうことかというと,次のようにclickイベントを割り当ててunbindするような処理で,もしclickイベントが複数割り当てられていたら,すべてのclickイベントが解除されてしまいます。

$('.class').bind('click', function(){//whatever});
$('.class').unbind('click');

これを防ぐために,jQueryでは一連のイベントに対して名前を付けておくことができるようにしています。click.namespaceのように記述することでイベントを識別可能になります。

$('.class').bind('click.namespace', function(){//}); 
$('.class').unbind('click.namespace');

1860行目でブラウザが識別できるイベント名をtypeに格納し,1861行目でnamespaceをhandler.typeに格納します。

1864行目は現在割り当てられているイベントハンドラを取得します。そして,もし既に割り当てられているイベントがあれば,初期化を行います。2126行目以降で定義されているready, mouseenter, mouseleave以外なら,addEventListener/attachEventを使って実際に要素にイベントを割り当てます。

1882:        // Add the function to the element's handler list
1883:        handlers[handler.guid] = handler;
1884:
1885:        // Keep track of which events have been used, for global triggering
1886:        jQuery.event.global[type] = true;
1887:      });
1888:    
1889:    // Nullify elem to prevent memory leaks in IE
1890:    elem = null;
1891:  },
1892:

要素のハンドラーリストにハンドラー関数を登録し,後からどのイベントが使われているかを追跡するためにglobal[type]に格納しておきます。最後にInternet Explorerのメモリリーク対策のためにelemを空にします。

著者プロフィール

山下英孝(やましたひでたか)

大学を卒業後,大手SIerに就職し,電機メーカーの研究所勤務を経て,ウノウに入社。1年半に渡ってWebサイトの開発,ディレクション,運用を経験した後に独立。2008年2月よりフリーエンジニアとして活動中。好きな言語はJavaScriptとPythonで,Linuxサーバ運用管理も得意。

ブログWeboo! Returns.

コメント

コメントの記入

パスサポ

多数の情報処理技術者試験対策書籍の発行実績を誇る技術評論社がお届けする,資格試験合格サイト「めざせ! 情報処理試験 パスサポ」が開設されました。

ピックアップ

サクセスストーリーに続く,快適サーバー運用管理のヒント!

データの増大,煩雑な管理,システムダウン,セキュリティなど,迫りくる課題からシステム管理者の負担を軽くするポイントを解説します。

gihyo.jp インフラエンジニア情報局

ネットワークやITにかかわるあらゆる業種で必要とされるインフラエンジニアに向けた技術情報や心構え,その魅力について多角的に紹介。

テストエンジニア ステーション

いま,ITに関わるあらゆる開発業務で注目されつつあるテスト系エンジニアをターゲットにしたコンテンツサイトを展開します。

一行クイックアンケート

gihyo.jpで取り上げてほしいネタは?

※検索はページ右上の検索ボックスをご利用ください。

その他の連載

もっと便利に!jQueryでラクラクサイト制作(実践サンプル付き)

本連載では,実践サンプルとともに,jQueryを上手に活用してサイト制作の品質向上・効率化を実現するための実践テクニックを解説します。

サクセスストーリーに続く,快適サーバー運用管理のヒント!

サーバーを自社で運用管理するのはもう限界…。データの増大,煩雑な管理,システムダウン,セキュリティなど,迫りくる課題からシステム管理者の負担を軽くするポイントを解説します。

続・先取り! Google Chrome Extensions

2010年1月のリリースが予定されているGoogle Chrome 4に搭載されるExtensionsについて,その詳細を先取りで解説します。最新情報から,ユーザースクリプトやテーマの作り方など関連情報もお届けします。

モダンPerlの世界へようこそ

この連載では,Perlの世代間ギャップに悩んでいる方に,いくらかの背景知識と,これだけは知っておいたほうがよいという最低限の慣用句をお届けします。

Hosting Department:ホスティングを活用するための基礎知識

本連載では,ホスティングサービスを活用する上で知っておきたい基礎知識を解説します。

Blogopolisから学ぶ計算幾何

計算幾何学は,図形に関するアルゴリズムを研究するコンピュータサイエンスの一分野です。本連載では,ビジュアルブログ検索エンジン「Blogopolis」で採用されている計算幾何のアプローチを例に取り上げながら,計算幾何の初歩を実践的に学習します。

Windows phoneアプリケーション開発入門

Windows Marcketplace for Mobileがサービス開始され,作成したアプリケーションを個人でも世界をターゲットに公開できる環境が整ってきました。これを機にWindows phoneアプリケーションの開発をしてみませんか?

いま,見ておきたいウェブサイト

この連載では,国内外の最新のウェブサイトを隔週更新で取り上げ,これら最新サイトの特徴や素晴らしい部分を,さまざまな角度から解説していきます。

連載一覧

gihyo.jp

  • DEVELOPER STAGE
  • ADMINISTRATOR STAGE
  • WEB+DESIGN STAGE
  • LIFESTYLE STAGE
  • SCIENCE STAGE
  • NEWS & REPORT

書籍案内

  • 新刊書籍
  • 書籍ジャンル一覧
  • 書籍シリーズ一覧
  • 新刊ピックアップ
  • ロングセラー
  • 電脳会議

定期刊行物一覧

  • Software Design
  • WEB+DB PRESS
  • Web Site Expert
  • 組込みプレス

最近のコメント