今回も前回に引き続き、イベント処理に関する処理の説明になります。
jQuery.event.trigger()
1962: trigger: function (type, data, elem, donative, extra) {
1963:
1964: data = jQuery.makeArray(data || []);
1965:
jQuery.event.trigger()メソッドは、イベントを実際に実行する処理です。第1引数typeには、"submit"や"click"などのイベントの種類を指定します。また、第2引数dataには、イベントハンドラに渡すデータを配列として指定します。第3引数elemは、イベントの動作対象となる要素です。第4引数donativeは、後ほど出てきますがブラウザのネイティブなイベント動作を行うかどうかのフラグです。そして、最後にextraが追加で実行する処理を指定します。
1964行では、引数として渡されたdataの複製を作成します。dataが指定されていなければ、空の配列を設定します。
1966:
1967: if ( !elem ) {
1968:
1969: if ( this .global[type] )
1970: jQuery("*" ).add([window, document]).trigger(type, data);
1971:
1967行目からは、第3引数elemが指定されていない場合の処理です。もし、グローバルなイベントが登録されていれば、window.documentに対してtypeイベントを実行します。
1972:
1973: } else {
1974:
1975: if ( elem.nodeType == 3 || elem.nodeType == 8 )
1976: return undefined;
1977:
1978: var val, ret, fn = jQuery.isFunction( elem[ type ] || null ),
1979:
1980: event = !data[0] || !data[0].preventDefault;
1981:
1982:
1983: if ( event )
1984: data.unshift( this .fix({ type: type, target: elem }) );
1985:
1986:
1987: data[0].type = type;
1988:
1975行目は、もうお馴染みの処理となりましたコメントノードとテキストノードの場合には、処理を行わないための条件式です。1978行目は、各変数を初期化しています。fnは引数として指定されたelem[type]が関数オブジェクトかどうかのチェック、eventはdata配列の第1要素にpreventDefaultというプロパティを持っているかどうかを判定しています。このpreventDefaultが指定されていなければ、1984行目にてダミーのイベントを準備します。1987行目は、イベントの種類をdata[0]に格納しています。
1989:
1990: if ( jQuery.isFunction( jQuery.data(elem, "handle" ) ) )
1991: val = jQuery.data(elem, "handle" ).apply( elem, data );
1992:
1993:
1994: if ( !fn && elem["on" +type] && elem["on" +type].apply( elem, data ) === false )
1995: val = false;
1996:
1990行目は、elemに対応するhandleが定義されていて、それが関数であればそれを実行して結果をvalに格納します。1994行目は、先ほどのelem[type]が直接実行できる関数ではない場合に、elem[on~]が実行できればそれを実行します。そして、戻り値がfalseであればvalにfalseを格納します。
1997:
1998: if ( event )
1999: data.shift();
2000:
2001:
2002: if ( extra && jQuery.isFunction( extra ) ) {
2003:
2004: ret = extra.apply( elem, val == null ? data : data.concat( val ) );
2005:
2006: if (ret !== undefined)
2007: val = ret;
2008: }
2009:
ダミーのイベントが登録されていれば、1999行目にてそれを削除します。
2002行目は、追加の関数が指定されている場合にそれを実行します。もし、valがnullならばdataを、あればdataとvalを結合して引数に指定します。そして、その戻り値ををretに格納します。
retにundefined以外の値が格納されていたら、2007行目にてvalにその値をセットしなおします。
2010:
2011: if ( fn && donative !== false && val !== false && !(jQuery.nodeName(elem, 'a' ) && type == "click" ) ) {
2012: this .triggered = true;
2013: try {
2014: elem[ type ]();
2015:
2016: } catch (e) {}
2017: }
2018:
2019: this .triggered = false;
2020: }
2021:
2010行目からは、リンクをクリックする以外のブラウザのネイティブなイベントを処理する部分になります。fnが定義済みで、donativeがfalse以外で、valがfalse以外で、aタグ要素のクリックイベント以外の場合に、this.triggeredの値をtrueにしてelem[ type ]()メソッドを実行します。
donativeの値によってネイティブなイベント動作、つまりブラウザのデフォルト動作を行うかどうかを決定しています。これは、jQuery.trigger()メソッドから呼び出された場合にはネイティブなイベントを実行しますが、jQuery.triggerHandler()メソッドから呼び出された場合はdonativeの値がfaleになるため、ブラウザのデフォルト動作は実行されないようになっています。
2022: return val;
2023: },
2024:
最後に2022行目で戻り値としてvalを返して終了です。
jQuery.event.handle()
2025行目からのjQuery.event.handle()メソッドは、イベント発生時に対応するハンドラを取り出してきて実行するメソッドです。
2025: handle: function (event) {
2026:
2027: var val;
2028:
2029:
2030: event = jQuery.event.fix( event || window.event || {} );
2031:
2032:
2033: var parts = event.type.split("." );
2034: event.type = parts[0];
2035:
2036: var handlers = jQuery.data(this , "events" ) && jQuery.data(this , "events")[event.type], args = Array.prototype.slice.call( arguments, 1 );
2037: args.unshift( event );
2038:
2027行目は、戻り値用の変数初期化です。
2029行目は、引数eventもしくはwindow.eventを後述のjQuery.event.fix()メソッドを通すことでブラウザ間の違いを吸収しています。
2033行目は、名前空間指定のイベントに関する処理で、foo.bar書式のfooの部分、すなわちイベント種別のみを取り出しています。
2036行目は、現在処理中の要素に登録されたイベント(events) 、もしくはそのイベントのevent.typeをhandler変数に代入します。また、argsに配列の2番目以降の要素を格納します。そして、2037行目で先ほどfix()を行ったeventを1番目に再代入しています。
2039: for ( var j in handlers ) {
2040: var handler = handlers[j];
2041:
2042:
2043: args[0].handler = handler;
2044: args[0].data = handler.data;
2045:
2046:
2047: if ( !parts[1] || handler.type == parts[1] ) {
2048: var ret = handler.apply( this , args );
2049:
2050: if ( val !== false )
2051: val = ret;
2052:
2053: if ( ret === false ) {
2054: event.preventDefault();
2055: event.stopPropagation();
2056: }
2057: }
2058: }
2059:
2039行目は、先ほど取得したハンドラ変数に対して処理を行います。2040行目でハンドラを取得し、後で削除できるように自身の変数にhandlerおよびdataを格納しておきます。
2047行目以降は、名前空間が指定されていないイベント、もしくは名前空間が現在処理中のhandlerと等しい場合に処理を行います。2048行目で実際にargsを引数としてhandler関数を実行し、ret変数に結果を格納します。retがfalseならば、fix()メソッドの実行時に定義されたpreventDefault()およびstopPropagation()メソッドを実行します。この2つのメソッドについては後ほど説明しますが、ブラウザのデフォルト動作を抑えるのと、コピーしたオブジェクトでもイベントが実行されるのを防ぐための処理です。
2060:
2061: if (jQuery.browser.msie)
2062: event.target = event.preventDefault = event.stopPropagation =
2063: event.handler = event.data = null;
2064:
2065: return val;
2066: },
2067:
2060行目からは、コメント文にもあるようにInternet Explorerのメモリーリークに対する対策です。event.target, event.preventDefault, event.stopPropagation, event.handler, event.dataにnullを代入しています。
2065行目にて、最後に結果のvalを返します。
jQuery.event.fix()
jQuery.event.fix()メソッドは、クロスブラウザを実現するためのメソッドです。
2068: fix: function (event) {
2069:
2070:
2071: var originalEvent = event;
2072: event = jQuery.extend({}, originalEvent);
2073:
2074:
2075:
2076: event.preventDefault = function () {
2077:
2078: if (originalEvent.preventDefault)
2079: originalEvent.preventDefault();
2080:
2081: originalEvent.returnValue = false;
2082: };
2083: event.stopPropagation = function () {
2084:
2085: if (originalEvent.stopPropagation)
2086: originalEvent.stopPropagation();
2087:
2088: originalEvent.cancelBubble = true;
2089: };
2090:
2091:
2092: if ( !event.target )
2093: event.target = event.srcElement || document;
2094:
2095:
2096: if ( event.target.nodeType == 3 )
2097: event.target = originalEvent.target.parentNode;
2098:
2099:
2100: if ( !event.relatedTarget && event.fromElement )
2101: event.relatedTarget = event.fromElement == event.target ? event.toElement : event.fromElement;
2102:
2103:
2104: if ( event.pageX == null && event.clientX != null ) {
2105: var doc = document.documentElement, body = document.body;
2106: event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc.clientLeft || 0);
2107: event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc.clientTop || 0);
2108: }
2109:
2110:
2111: if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) )
2112: event.which = event.charCode || event.keyCode;
2113:
2114:
2115: if ( !event.metaKey && event.ctrlKey )
2116: event.metaKey = event.ctrlKey;
2117:
2118:
2119:
2120: if ( !event.which && event.button )
2121: event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
2122:
2123: return event;
2124: },
2125:
2071~2071行目は、元のイベントをoriginalEvent変数に格納し、jQuery.extend()メソッドを使って再度event変数に代入することで、読み込み専用のプロパティとして設定します。
2076~2082行目は、今までに何度か登場しているevent.preventDefault()メソッドで、複製したオブジェクトにおいてブラウザのデフォルト動作が行われるのを抑制します。こちらもInternet Explorerのための処理です。
2083行目~2089行目は、event.stopPropagation()メソッドで、originalEventのメソッドを実行した後にcancelBubbleをtrueに設定します。こちらもInternet Explorerのための処理です。
2092行目~2093行目は、event.targetが未設定の場合にevent.srcElementが存在すればevent.srcElementを、なければdocumentオブジェクトをevent.targetに設定します。
2096~2097行目は、ターゲットがテキストノードの場合にtargetを親ノードに変更します。Safariブラウザ用の対策になります。
2100行目~2101行目は、relatedTargetがなくてfromElementが存在する場合にevent.relatedTargetの値を設定します。
2104行目~2108行目は、pageXが存在せずにclientXプロパティがあるようなブラウザ(つまりInternet Explorer)の場合にpageXおよびpageYの値を計算します。
2111行目~2112行目は、マウスクリックやキー押下時のイベントをevent.whichに統一します。
2115行目~2116行目は、Mac以外でctrlKeyのイベントをmetaKeyとして取得できるようにします。
2120行目~2121行目は、マウスのどのボタンが押されたかをevent.whichとして取得できるようにします。
jQuery.event.special.ready
2126行目からは、jQuery.event.specialというイベント処理の中でも特別な処理をまとめた部分になります。jQuery.event.specialにはready,mouseenter,mouseleaveの3つの属性があり、それぞれsetup(),teardown(),handler()という3つのメソッドを持っています(ただし、readyにhandlerはありません) 。setup()はイベント登録用、teardown()はイベント解除用、handler()はイベントハンドラー用のメソッドになります。それでは、これらのメソッドを順番に見ていきましょう。
2126: special: {
2127: ready: {
2128: setup: function () {
2129:
2130: bindReady();
2131: return ;
2132: },
2133:
2134: teardown: function () { return ; }
2135: },
2136:
2128行目のsetup()メソッドは、2284行目で定義されているbindready()メソッドを呼び出します。イベント解除を行うteardown()では特になにもせずに、そのままreturnします。
jQuery.event.special.mouseenter
2137: mouseenter: {
2138: setup: function () {
2139: if ( jQuery.browser.msie ) return false;
2140: jQuery(this ).bind("mouseover", jQuery.event.special.mouseenter.handler);
2141: return true;
2142: },
2143:
2144: teardown: function () {
2145: if ( jQuery.browser.msie ) return false;
2146: jQuery(this ).unbind("mouseover", jQuery.event.special.mouseenter.handler);
2147: return true;
2148: },
2149:
2150: handler: function (event) {
2151:
2152: if ( withinElement(event, this ) ) return true;
2153:
2154: arguments[0].type = "mouseenter";
2155: return jQuery.event.handle.apply(this , arguments);
2156: }
2157: },
2158:
2137行目からのjQuery.event.special.mouseenterは、setup()メソッドでInternet Explorer以外の場合に指定要素の"mouseover"イベントにハンドラを割り当てます。teardown()でも同様にunbindでイベント登録を解除します。2150行目からのhandler()メソッドは、実際のイベントハンドラの定義になります。2152行目にてwithinElement()関数を呼び出しているのは、内包している要素があった場合に通常ですとその上でマウスを移動させるとmouseover/outイベントが発生してしまいますが、それを防ぐための処理です。そして、2155行目で引数にmouseenterを指定した上で、jQuery.event.handleメソッドを呼び出します。
Internet Explorerの場合は、attachEventおよびdetachEventを利用するため、ここでは何もせずにfalseを返します。
jQuery.event.special.mouseleave
2159: mouseleave: {
2160: setup: function () {
2161: if ( jQuery.browser.msie ) return false;
2162: jQuery(this ).bind("mouseout", jQuery.event.special.mouseleave.handler);
2163: return true;
2164: },
2165:
2166: teardown: function () {
2167: if ( jQuery.browser.msie ) return false;
2168: jQuery(this ).unbind("mouseout", jQuery.event.special.mouseleave.handler);
2169: return true;
2170: },
2171:
2172: handler: function (event) {
2173:
2174: if ( withinElement(event, this ) ) return true;
2175:
2176: arguments[0].type = "mouseleave";
2177: return jQuery.event.handle.apply(this , arguments);
2178: }
2179: }
2180: }
2181: };
2182:
2159行目からのjQuery.event.special.mouseleaveもmouseenterと同様です。setup()メソッドでInternet Explorer以外の場合に指定要素の"mouseout"イベントにハンドラを割り当てます。teardown()ではunbindを使ってイベント登録を解除します。2172行目からのhandler()メソッドは、実際のイベントハンドラの定義になります。2174行目にてwithinElement()関数を呼び出しているのもmouseenterの時と同様で、内包している要素があった場合に通常ですととその上でマウスを移動させるとmouseover/outイベントが発生してしまいますが、それを防ぐための処理です。そして、2177行目で引数にmouseleaveを指定した上で、jQuery.event.handleメソッドを呼び出します。
Internet Explorerの場合は、attachEventおよびdetachEventを利用するため、ここでは何もせずにfalseを返します。