JavascriptのEvent Handlerの引数で受け取るEvent、多言語で言うところの"EventArgs"をクロスブラウザに対応させる。
例えば、以下のようなイベントハンドラがあるとする。
function clickEventHandler(event){ alert('clicked.'); event.stopPropagation(); }このままではIEの場合、stopPropagationというメソッドはeventのメンバにないので、イベント伝播を止められない。
これに対応するために通常なら『event.cancelBubble = true;』を書き加えるだろう。
つまり、どう対応するかは予め決まっている。それならば、事前にそのような処理に書き換えれば良いのではないか?と考えた。
イベント伝播を止めるには以下のような処理を行う。
if(event.stopPropagation){ event.stopPropagation(); }else{ event.cancelBubble = true; }stopPropagationメソッド自体を上のような処理を行うように書き換えてしまえば、イベントハンドラ側でブラウザの違いを気にする必要がなくなり、IEでもそれ以外のモダンブラウザでもstopPropagationメソッドを使えばイベント伝播を止めることが出来るようになる。
まず、イベントハンドラをフックするための関数。
function addEvent(elm, type, fn){ function handler (){ arguments[0] = eventFix( arguments[0] || window.event ); fn.apply( this, arguments ); } //Modern Browser if (document.addEventListener) { elm.addEventListener(type, handler, false); } //IE else if (document.attachEvent) { elm.attachEvent('on' + type, handler); } else { elm['on' + type] = handler; } }
『イベント発生 → fn』
↓
『イベント発生 → hendler → fn』
handler関数を間に挟むと考えると分かりやすいだろうか。
このhandler関数で下記のeventFix関数を呼び出し、eventを書き換えてからfn関数に渡す。という流れ。
ブラウザ間の違いを吸収するための関数
function eventFix(e){ var //Event Clone cloneEvent = {}, //Original Event originalEvent = e; //Copy Event for (var prop in originalEvent) { cloneEvent[prop] = originalEvent[prop]; } /* stopPropagation * イベントフローにおいてこれ以上イベントが伝えられるのを止める。 */ cloneEvent.stopPropagation = (function(){ return originalEvent.stopPropagation ? function(){ originalEvent.stopPropagation(); } : function(){ originalEvent.cancelBubble = true; //IE }; })(); /* preventDefault * イベントがキャンセル可能な場合、 * そのイベントの結果として通常は実装により実行される * デフォルトのアクションをキャンセルする。 */ cloneEvent.preventDefault = (function(){ return originalEvent.preventDefault ? function(){ originalEvent.preventDefault(); } : function(){ originalEvent.returnValue = false; //IE }; })(); return cloneEvent; }
HTML:
<html> <body> <div id='wrap'><span>wrap</span> <a id='button' href="javascript:void(0)">Button</a> </div> </body> </html>
Usage:
addEvent(window, 'load', function(){ addEvent(document.getElementById('button'), 'click', function(e){ alert( 'Button Clicked.' ); e.stopPropagation(); //e.preventDefault(); }); addEvent(document.getElementById('wrap'), 'click', function(e){ alert( 'Wrap Clicked.' ); }); });Buttonエレメントをクリックしても親要素であるWrapエレメントに関連付けられたclickイベントは発生しない。
もちろんこれは完璧なものではない。ただ、こうすれば可能、というもので使用可能なレベルかは分からない。
先人さんが既に実装していた
これを書きながら「私が考えることなど先人が既に考えたのでは?」ということに気付き、ちょっと調べるとjQueryでは既に対応していることが分かった。jQuery.Event辺りのソースを見ると、実装されていることが分かる。
例えば、jQueryでは押されたキーのkeycodeを取得するにはevent.whichで統一して取得できるようになっている。
ということで...
jQueryを使えばeventもクロスブラウザに対応しているので、ブラウザ間の違いを気にせずに書ける。
0 Comments:
コメントを投稿