メインコンテンツへ移動

JS 原生イベント処理(クロスブラウザ)

無料2015-03-22#JS#JS原生事件处理

他人の JS ライブラリやフレームワークを使わずに、クロスブラウザのイベント処理プログラムを書けますか?

##一.イベントオブジェクトの取得について##

FF は少し頑固で、arguments[0] のみをサポートし、window.event をサポートしません。今回は本当に IE を責められません。event を window のプロパティとするのは規格に合致しませんが、誰もがすでにこの小さな問題を黙認しており、FF だけが这么多年経っても特立独行です。したがって、クロスブラウザのイベントオブジェクト取得には以下の 2 種類の方法があります:

  1. パラメータあり:

    getEvent : function(event){
        return event ? event : window.event;
        //return event || window.event;//またはより簡単な方法
    }
    
  2. パラメータなし:

    function getEvent() {
        return arguments[0] ? arguments[0] : window.event;
        //return arguments[0] || window.event;//またはより簡単な方法
    }
    

特に説明が必要な方式が 1 つあります:HTML の DOM0 級方式 + パラメータ付きイベントハンドラ、以下の通り:

function handler(event){
    //do something
}

<!-- HTML の DOM0 級方式 -->
<button id="btn" onclick="handler(event);">ボタン</button>

上記の方式は全ブラウザ互換ですが、HTML の DOM0 級方式に依存する欠点が明らかであるため、前 2 つのような主流方法にはなりませんでした。一方、JS の DOM0 級方式 + パラメータ付きイベントハンドラは以下の通り:

function handler(event){
    //do something
}
btn.onclick = handler;//JS の DOM0 級方式
//btn.onclick = function(event){/*do something*/}//または無名関数、効果は同上

この方式は全ブラウザ互換ではありません、[IE8-] はサポートせず、IE9+ は不明、FF、Chrome はサポートします。ずっと HTML の DOM0 級イベント処理と JS の DOM0 級イベント処理は等価だと思っていましたが、多くの実験をして初めて両者に違いがあることに気づきました

##二.イベントソースの取得について##

event.srcElement は [IE8-] 独自の方式で、IE9+ は不明、他のブラウザはすべて標準の event.target 方式をサポートします

##三.イベントのデフォルト動作を阻止することについて##

event.preventDefault() は標準メソッドですが、[IE8-] はサポートせず、IE 独自の方式は event.returnValue = false; です

##四.イベント伝播を停止することについて##

event.stopPropagation() は標準メソッドですが、IE はまた意見があり、こうプレイします:event.cancelBubble = true;ここで特に注意が必要です、cancel はメソッドではなくプロパティであるため、標準とは大きく異なり、覚え間違いやすいです

##五.イベントハンドラの追加と削除について##

  1. DOM0 級方式

ele.onclick = handler;ele.onclick=null;最も古い方式

利点:全ブラウザ互換

欠点:同一イベントに 1 つのイベントハンドラのみバインド/アンバインド可能

  1. DOM2 級方式

ele.add/removeEventListener(eventType, handler, catch);

と IE 方式:ele.attach/detachEvent('on'+eventType, handler);

利点:複数のイベントハンドラのバインド/アンバインドをサポート

欠点:互換性判断が必要。注意すべき点は:標準方式では最後のパラメータはイベントキャプチャ段階でバインド/アンバインドするかどうかを示し、IE はイベントキャプチャをサポートしないため、3 番目のパラメータもありません

注意:IE 方式はメソッド名が標準と異なるだけでなく、パラメータ中のイベントタイプにも on を追加する必要があります、そうでないとバインドは無効ですがエラーは出ません

##六.クロスブラウザのイベント処理##

//クロスブラウザのイベントハンドラ追加方式
var EventUtil = {
    addHandler : function(elem, type, handler){
        if(elem.addEventListener){
            elem.addEventListener(type, handler, false);
        }
        else if(elem.attachEvent){
            elem.attachEvent("on" + type, handler);//複数の同一タイプの handler を追加する場合、IE 方式のルールは最後に追加したものが最初にトリガーされる
        }
        else{
            if(typeof elem["on" + type] === 'function'){
                var oldHandler = elem["on" + type];
                elem["on" + type] = function(){
                    oldHandler();
                    handler();
                }
            }
            else{
                elem["on" + type] = handler;//複数のイベントハンドラの追加をサポート
            }
        }
    },

    getEvent : function(event){
        return event ? event : window.event;
    },

    getTarget : function(event){
        return event.target || event.srcElement;
    },

    preventDefault : function(event){
        if(event.preventDefault){
            event.preventDefault();
        }
        else{
            event.returnValue = false;
        }
    },

    removeHandler : function(elem, type, handler){
        if(elem.removeEventListener){
            elem.removeEventListener(type, handler, false);
        }
        else if(elem.detachEvent){
            elem.detachEvent("on" + type, handler);
        }
        else{
            elem["on" + type] = null;//単一のイベントハンドラの削除をサポートせず、すべて削除するのみ
        }
    },

    stopPropagation : function(event){
        if(event.stopPropagation){
            event.stopPropagation();
        }
        else{
            event.cancelBubble = true;
        }
    },

    getRelatedTarget : function(event){
        if(event.relatedTarget){
            return event.relatedTarget;
        }
        else if(event.toElement && event.type == "mouseout"){
            return event.toElement;
        }
        else if(event.fromElement && event.type == "mouseover"){
            return event.fromElement;
        }
        else{
            return null;
        }
    },

    /*IE8 は左ボタンと中ボタンをクリックしてもどちらも 0;FF は中ボタンを識別不可;Chrome は正常*/
    getButton : function(event){//0,1,2 を返す - 左、中、右
        if(document.implementation.hasFeature("MouseEvents", "2.0")){
            return event.button;
        }
        else{
            switch(event.button){
                case 0:case 1:case 3:case 5:case 7:
                    return 0;
                    break;
                case 2:case 6:
                    return 2;
                    break;
                case 4:
                    return 1;
                    break;
                default:
                    break;
            }
        }
    },

    /*keypress イベントのみ検出可能、戻り値は表示される文字コードに等しい*/
    /*IE と Chrome は表示可能な文字キーのみトリガー、FF は他のキーもトリガー可能、戻り値は 0*/
    getCharCode : function(event){
        if(typeof event.charCode == "number"){
            return event.charCode;
        }
        else{
            return event.keyCode;
        }
    }
};

上記のコードは『JavaScript 高級程序设计』中文版から整理したもので、书中の getRelatedTarget に少し問題があります(P373)、本文ですでに修正済みです

书中の addHandler は DOM0 級方式で複数のイベントハンドラを追加することをサポートしていません、修正後はサポート可能ですが、単一のイベントハンドラの削除をサポートしないため、コードは依然として不完全です。DOM0 級イベントハンドラの追加/削除操作をサポートしたい場合、手動でイベントハンドラキューを維持できますが、この方式を実現することに大きな意味がないため、ここでは展開しません

###参考資料###

コメント

コメントはまだありません

コメントを書く