prototype.jsを読み解く

第10回 Prototypeライブラリ(2846~3276行目)

この記事を読むのに必要な時間:およそ 15 分

Event オブジェクトへの拡張

2941: if (!window.Event) {
2942:   var Event = new Object();
2943: }
2944: 

2941行目からはEventオブジェクトです。IEではグローバルオブジェクトとしてEventが存在しますが,それ以外の場合は空のオブジェクトとして作成しておきます。

2945: Object.extend(Event, {
2946:   KEY_BACKSPACE: 8,
2947:   KEY_TAB:       9,
2948:   KEY_RETURN:   13,
2949:   KEY_ESC:      27,
2950:   KEY_LEFT:     37,
2951:   KEY_UP:       38,
2952:   KEY_RIGHT:    39,
2953:   KEY_DOWN:     40,
2954:   KEY_DELETE:   46,
2955:   KEY_HOME:     36,
2956:   KEY_END:      35,
2957:   KEY_PAGEUP:   33,
2958:   KEY_PAGEDOWN: 34,
2959: 

2945行目からはObject.extend()を使ってEventオブジェクトを拡張しています。prototypeではなくEvent直下に拡張しているので,Event.element()などのように呼び出す形となります。

まずはキーコードを名前で参照するために数値との対応を定義しています。

2960:   element: function(event) {
2961:     return $(event.target || event.srcElement);
2962:   },
2963: 

2960行目からはEvent.element()関数です。

イベントが発生した要素を返します。IE系はsrcElementに,他の多くはtargetプロパティに入っているので,どちらかを返すようにしています。

2964:   isLeftClick: function(event) {
2965:     return (((event.which) && (event.which == 1)) ||
2966:             ((event.button) && (event.button == 1)));
2967:   },
2968: 

2964行目からはisLeftClick()です。

Mozilla系ではwhichプロパティに1が入っていれば左クリック,IE系ではbuttonプロパティが1なら左クリックだけのクリックになります(1,2,4の論理和)⁠どちらかが真なら左クリックとしています。

したがって,IE系では右クリックしながら左クリックなどは真になりません。また,IEではそもそもonMouseDown, onMouseUp, onMouseMoveイベントでしかbuttonプロパティに値が入りません。

2969:   pointerX: function(event) {
2970:     return event.pageX || (event.clientX +
2971:       (document.documentElement.scrollLeft || document.body.scrollLeft));
2972:   },
2973: 
2974:   pointerY: function(event) {
2975:     return event.pageY || (event.clientY +
2976:       (document.documentElement.scrollTop || document.body.scrollTop));
2977:   },
2978: 

pointerX(), pointerY()関数です。イベントが発生した場所の,ページ上の位置を返します。スクロールしていてもいなくても,ある要素の位置は同じ値になります。

Mozilla系ではevent.pageX, event.pageYプロパティで該当する値を取得できます。

IE系では,event.clientXにクライアント領域内のオフセットが入ります。後方互換モードではdocument.body.scrollLeftに,標準準拠モードではdocument.documentElement.scrollLeftにクライアント領域のスクロールオフセットが入るので,ページ内の座標に変換するためにそれらのどちらかを足します。

2979:   stop: function(event) {
2980:     if (event.preventDefault) {
2981:       event.preventDefault();
2982:       event.stopPropagation();
2983:     } else {
2984:       event.returnValue = false;
2985:       event.cancelBubble = true;
2986:     }
2987:   },
2988: 

イベントキャンセル用のEvent.stop()です。

Mozilla, Safari,OperaはDOM準拠のpreventDefault()メソッドが使えるので,こちらを呼び出しイベントのデフォルトの挙動(ボタンのonClickでの動作など)をキャンセルし,stopPropagation()でイベントの伝播を停止します。

一方IEでは,event.returnValueにfalseを入れることでデフォルトの挙動をキャンセルし,event.cancelBubbleにtrueを入れることでイベントの伝播を停止します。

2989:   // find the first node with the given tagName, starting from the
2990:   // node the event was triggered on; traverses the DOM upwards
2991:   findElement: function(event, tagName) {
2992:     var element = Event.element(event);
2993:     while (element.parentNode && (!element.tagName ||
2994:         (element.tagName.toUpperCase() != tagName.toUpperCase())))
2995:       element = element.parentNode;
2996:     return element;
2997:   },
2998: 

2991行目からはEvent.findElement()です。

イベントターゲットの要素ではなく,その親のうち,指定されたタグ名を持つ直近の要素を返します。

while文でparentNodeがnullの場合は,自身がdocumentノードなのでdocumentノードを返し,tagNameプロパティが指定されたものと同じであればその要素を返します。そうでなければparentNodeをたどります。

2999:   observers: false,
3000: 

observersは初期値はfalseですが,使われているときは配列となります。これは後述するIEのメモリリーク対策として,イベントを登録するときに一旦保存しておき,ページのonUnload時にdetachEvent()する,という処理を行うためです。

3001:   _observeAndCache: function(element, name, observer, useCapture) {
3002:     if (!this.observers) this.observers = [];
3003:     if (element.addEventListener) {
3004:       this.observers.push([element, name, observer, useCapture]);
3005:       element.addEventListener(name, observer, useCapture);
3006:     } else if (element.attachEvent) {
3007:       this.observers.push([element, name, observer, useCapture]);
3008:       element.attachEvent('on' + name, observer);
3009:     }
3010:   },
3011: 

Event.observe()から呼ばれる内部関数 _observeAndCache()です。名前にCacheとありますが特にキャッシュ的な機能を持つわけではなく,イベントを解除するために保存しておく機能があるだけです。

IEとそれ以外ではイベントの登録方法が違うので,addEventListener()が存在すればDOM準拠の方法で,attachEvent()が存在すればIEでの方法でイベントを登録します。

また,次のunloadCache()で使うためにthis.observersに渡された情報を保存しておきます。

著者プロフィール

栗山淳(くりやまじゅん)

S2ファクトリー株式会社株式会社イメージソース所属。
本業はWeb制作会社の裏方。得意分野はFreeBSDやPerlのはずだが,必要に迫られるとHTML/CSSやJavaScriptも書く。

コメント

コメントの記入