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:
コメントを投稿