본문으로 건너뛰기

JS 네이티브 이벤트 처리 (크로스 브라우저)

무료2015-03-22#JS#JS原生事件处理

타인의 JS 라이브러리와 프레임워크 없이 크로스 브라우저 이벤트 처리 프로그램을 작성할 수 있는가?

##一.이벤트 객체 취득에 대하여##

FF 는 조금 고집스러워서 arguments[0] 만 지원하고 window.event 는 지원하지 않습니다. 이번엔 정말 IE 를 탓할 수 없습니다. event 를 window 의 속성으로 하는 것은 규격에 맞지 않지만, 모두가 이미 이 작은 문제를 묵인하고 있으며, FF 만이这么多年이 지나도 특이하게独行합니다. 따라서 크로스 브라우저 이벤트 객체 취득에는 다음과 같은 두 가지 방식이 있습니다:

  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;//또는 더 간단한 방식
    }
    

특별히 설명해야 할 방식이 하나 있습니다: HTML 의 DOM0 급 방식 + 매개변수 있는 이벤트 핸들러, 다음과 같습니다:

function handler(event){
    //do something
}

<!-- HTML 의 DOM0 급 방식 -->
<button id="btn" onclick="handler(event);">버튼</button>

위의 방식은 전 브라우저 호환이지만, HTML 의 DOM0 급 방식에 의존하는 단점이 분명하므로 앞의 두 가지 같은 주류 방식이 되지 못했습니다. 반면 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. DOM2 급 방식

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

와 IE 방식: ele.attach/detachEvent('on'+eventType, handler);

장점: 여러 이벤트 핸들러의 바인드/언바인드 지원

단점: 호환성 판단이 필요합니다. 주의할 점은: 표준 방식에서 마지막 매개변수는 이벤트 캡처 단계에서 바인드/언바인드할지 여부를 나타내며, IE 는 이벤트 캡처를 지원하지 않으므로 세 번째 매개변수도 없습니다

주의: 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 급 이벤트 핸들러의 추가/제거 작업을 지원하려면 수동으로 이벤트 핸들러 큐를 유지할 수 있지만, 이 방식을 구현하는 데 큰 의미가 없으므로 여기서는 전개하지 않습니다

###참고 자료###

댓글

아직 댓글이 없습니다

댓글 작성