본문 바로가기
Javascript

자바스크립트(javascript) 웹 브라우저 스크립팅. [2]

by 램쥐뱅 2017. 8. 29.

DOM Level 2 - 이벤트 전파


W3C에서는 웹 브라우저 애플리케이션에서 이벤트 처리를 표준화하기 위해 "DOM Level2 이벤트 모델"이라는 표준을 제시했다.


자바스크립트(javascript) 웹 브라우저 스크립팅. [1] 에서 정리해본 핸들링 방식은 W3C에서 제시한 DOM Level 0을 기반으로 한 것이다.

DOM Level 2 이벤트 모델에는 이벤트를 등록하거나 제거하는 표준적인 방법을 비롯해 이벤트 전파 메커니즘에 대한 표준, 이벤트 핸들링 메서드로 전달되는 인벤트 객체에 대한 표준 등이 포함되어 있다. 물론 DOM Level 2의 모델을 지원하는 거의모든 브라우저는 DOM Level 0 의 표준도 지원할 것이다. (IE 는 9부터 표준을 지키고 있다.)


많은 자바스크립트 라이브러리는 DOM Level 2에서 제시하는 문법 스타일을 따른다. 따라서 자바스크립트 라이브러리를 공부하기 전에 DOM Level 2의 구문과 객체를 알아보면 도움될 것이다.


DOM Level 2 에서 정의한 이벤트 전파 모델을 살펴본다.


1. 이벤트가 브라우저에서 발생.

2. DOM 의 최상위 객체인 Document 객체로 이벤트가 전달.

3. 이벤트를 발생시킨 요소에 해당하는 DOM 객체로 이벤트가 전달.

4. 해당 객체에 등록된 이벤트 핸들러가 호출.


이벤트는 해당 객체의 핸들러에서 처리될 수도 있지만 그 객체의 부모 객체로 전파되어 처리될 수도 있다.


이처럼 이벤트가 흘러가는 과정을 이벤트 흐름(event flow)이라고 한다.

이벤트 흐름을 그림으로 그려보면 다음과 같다. 중요!



이벤트 흐름을 나타내는 그림을 보면 크게 3단계를 거친다.


만약 브라우저의 <td>에서 이벤트가 발생하면 그 이벤트는 우선 브라우저에서 Document 객체로 전달되어 이벤트가 발생된 <td>의 부모 객체까지 전달되는 것을 캡처링(Capturing) 이라고 한다.


이벤트를 발생시킨 요소에 해당하는 DOM 객체를 타겟 객체(Target)라고 한다.


타겟 객체로 전달된 다음 다시 이벤트는 <td> 객체의 부모 객체로 버블링(Bubbling) 된다.


캡처링 단계 (capture phase) : 이벤트가 문서의 루트 객체인 Document를 거쳐 이벤트가 발생한 타겟 객체의 부모 객체까지 전달되는 단계.


타겟 단계(target phase) : 이벤트가 발생한 객체로 전달되는 단계.


버블린 단계(bubbling phase) : 타겟 객체의 부모에서 Document 객체까지 전달되는 단계.


만약 브라우저에서부터 타겟의 부모 객체까지 전달된 이벤트를 중간 단계, 즉 캡처링 단계에서 처리하고 싶다면 중간 요소에 이벤트 핸들러를 등록하면 된다.


이벤트를 캡처링 단계에서 호출되게 할지, 아니면 타겟 단계와 버블링 단게에서 호출되게 할지는 이벤트 핸들러를 등록할 때의 옵션에 달렸다.


현실적으로 타겟 단계, 즉 이벤트를 발생시킨 요소에서 이벤트를 처리하는 것이 가장 흔한 처리 방식이다.

그러나 버블링 단계에서 이벤트를 처리하는 방식도 많이 사용되고, 캡처링 단계에서 처리되는 경우도 있다.




DOM Level 2 - 이벤트 핸들링


addEventListener(), removeEventListener()


다음 방법은 2000년 11월에 W3C가 발표한 DOM Level 2  이벤트 모델에서 제시한 이벤트 등록 방법이다.


element.addEventListener(string type, Function handler, boolean useCapture);

element.removeEventListener(string type, Function handler, boolean useCapture);


 매개변수 

 설명 

 type

 핸들링하고 싶은 이벤트. "load", "click", "mousedown'

 handler

 지정한 타입의 이벤트가 현재 요소로 전달되명 호출될 이벤트 핸들링 함수.

 핸들러가 호출되명 Event 객체가 핸들러로 전달된다.

 useCapture

 true : 이벤트 핸들러는 캡처링 단게에서만 호출된다.

 false : 타겟 단계와 버블링 단게에서 호출된다. 보통 false를 사용한다.


이벤트가 발생해서 핸들러가 콜백될 때는 이벤트에 대한 정보가 담긴 객체가 핸들러에 인자로 전달된다.


element.addEventListener("click", fucntion(e){alert("click");}, false);


element.addEventListener("click", myHandler, false);


function myHandler(e){

alert(e.target.id + "is called");

}


addEventListener() 함수를 여러 번 호출해서 같은 요소, 같은 이벤트에 대해 여러 개의 이벤트 핸들러를 등록할 수 있다. 그러나 호출되는 순서는 보장되지 않는다.


addEventListener 로 등록된 핸들러는 removeEventListener 를 통해 해당 요소의 이벤트 타입에 대한 핸들러 목록에서 제거할 수 있다.


element.removeEventListener("click", myHandler, false);



캡처링 단계의 이벤트 핸들러 등록


위에서 이벤트 핸들러를 등록하는 방법은 모두 캡처링 단게를 무시한다. 요소의 click 이벤트에 핸들러를 등록했다고 해보자.


<div class="d1">div1

<div class="d2">div2

<div class="d3">div3

</div>

</div>

</div>


div2에 대한 <div> 요소를 클릭하더라도 캡처링 단계에서 div1에 대한 <div>에 등록된 이벤트 핸들러는 호출되지 않는다.

캡처링 단계에서 이벤트를 잡는 유일한 방법은 DOM Level 2 모델에서 제시하는 addEventListener 를 이용하는 방법밖에 없다. 이 함수의 마지막 매개변수인 useCapture를 true로 설정하면 된다.


만약 d1에 다음과 같이 캡처링 단계에서 click 이벤트를 잡겠다고 설정해보자.


div1.addEventListener("click", function(){ alert('d1'); }, true);


이러한 상태에서 d2에 대한 <div>를 클릭하면 click 이벤트는 d1 -> d2로 흘러가게 된다.

이때 d1에 대한 <div>에 등록된 click에 대한 이벤트 핸들러가 호출된다.



버블링 단계의 핸들러 이용


만약 useCapture 값을 false로 지정하면 해당 핸들러는 타겟 간계, 버블링 단계에서 호출된다.

이벤트 버블링은 <div> 같은 컨테이너 요소를 이용해 내부에서 발생하는 이벤트를 모두 한 곳에서 관리하는 구조로 코드를 작성할 때 편리하다. 예를 들어, 가장 외곽의 <div>에 다음과 같은 이벤트 핸들러를 등록해서 이곳에서 내부의 모든 <div>에 대한 click 이벤트를 처리할 수 있다.


div1.addEventListener("click", myHandler, false);


function myHandler(e){

e = e || event; // 브라우저 호환성을 위한 코드

var target = e.target || e.srcElement; // 브라우저 호환성을 위한 코드


if(e.target.className == 'd3'){

//div3 핸들링

}else if(e.target.className == 'd2'){

//div2 핸들링

}else if(e.target.className == 'd1'){

//div1 핸들링

}

}

                                                                                                                                                        가장 외곽의 div1에 대한 <div>의 click 이벤트의 핸들러로 등록된다. 이 이벤트 핸들러에서는 click 이벤트를 발생시킨 요소(e.target)가 어느 div인지 판단해서 적절한 핸들링 코드를 실행 한다. 이렇게 이벤트 버블링을 사용하면 하나의 핸들러에서 여러 요소의 이벤트를 핸들링할 수 있다.


타겟 객체에 이벤트 핸들러가 등록되어 있지 않다면 바로 버블링 단계로 접어든다.

모든 이벤트가 이런 단계를 거치는 것은 아니다. 어떤 이벤트는 캡처링 단계, 타겟 단계는 거치지만 버블링 단계를 거치지 않기도 한다.     



타겟 요소의 이벤트 핸들링


useCapture 를 false로 지정하면 이벤트는 타겟에서 부모 객체로 흘러 올라간다.

만약 타겟 단계에서 발생하는 이벤트에 대해서만 실행되고 하위 요소에서 버블링되는 이벤트는 무시하는 이벤트 핸들러를 만들고 싶다고 하자. 즉, 내부 div2, div3 에 대한 <div>가 아닌 외부 div1에 대한 <div>가 직접 클릭되는 경우에만 이벤트 핸들러가 호출되기를 원한다는 것이다. 이 경우는 이벤트 핸들러를 등록할 때의 옵션으로는 해결할 수 없고, 코드 내부에서 해결해야 한다.


div1.addEventListener("click", myHandlerfalse);


function myHandler(e){

e = e || event; // 브라우저 호환성을 위한 코드

var target = e.target || e.srcElement; // 브라우저 호환성을 위한 코드


if(e.target == this){

//핸들링

}

}


이벤트가 발생한 객체를 나타내는 이벤트의 target과 this를 비교하고 있다. 이 비교를 통해 이벤트가 버블링되는 것인지 아니면 현재 요소에서 발생한 것인지를 판단할 수 있다.


event.target == this 를 이용하면 이벤트가 자신에게서 발생한 것인지 아니면 내부 자식에서 발생한 것인지 판별할 수 있다.



타겟 요소 이벤트 버블링 막기


div1, div2, div3 모두 이벤트에 대한 이벤트 핸들러가 등록되었을때 dvi3 요소를 클릭하면 이벤트 버블링 때문에 dvi2, div1의 이벤트 핸들러도 같이 실행이 된다.


이러한 이벤트 버블링을 막을수 있는 방법이 존재한다.


event.stopPropagation(); : 이벤트가 부모로 버블링 되는것을 막아 준다.

event.stopImmediatePropagation(); : 같은 이벤트가 여러개 등록되 있을때 같은 이벤트에 대한 다른 리스너들 호출을 막아준다.




DOM Level 2 - Event 객체


이벤트가 발생하면 이벤트 정보가 담긴 Event 객체를 구성하게 되는데, 이 객체를 인자로 이벤트 핸들러를 호출하게 된다.

프로그램에서는 이 객체를 통해 이벤트 정보에 접근할 수 있다.


 속성

 설명

 type

 발생한 이벤트의 타입. "click", "keydown"처럼 이벤트 핸들러를 등록할 때 사용한 속성명과 동일한 문자열.

 target

 원래 이벤트가 발생한 요소로서 다음에 설명하는 currentTarget과는 다를 수 있다.

 currentTarget

 이벤트가 처리되고 있는 요소로서, 현재 실행되고 있는 핸들러가 등록된 요소를 반환한다.

 만약 캡처링 또는 버블링하는 동안 이벤트가 처리된다면 이 속성의 값은 원래 이벤트가 발생한 요소를 나타내는  target과는 다를수 있다.

 bubbles

 버블링 이벤트인지를 나타낸다. 버블링되는 이벤트 : true, 그렇지 않은 경우 : false

 cancelable

 preventDefault() 메소드로 취소될 수 있는 기본 액션이 존재하는지 알려준다.

 eventPhase

 이벤트 전파가 어떤 단계인지를 알려주는 상수값. 다음중 하나를 값으로 취한다.

 Event.CAPTURING_PHASE

 Event.TARGET

 Event.BUBBLING_PHASE


 메서드

 설명

 preventDefault()

 브라우저가 이벤트와 관련된 기본 액션을 수행하지 않게한다.

 ex) <a> 태그의 href 속성은 링크의  href가 가리키는 주소로 이동하는것이 기본 액션.

      그러한 태그 객체의 기본액션을 막아준다.

 stopPropagation()

 현재 핸들링되고 있는 객체 이상으로 이벤트가 전파되지 않게 한다.

 stopImmediatePropagation()

 같은 이벤트에 여러 핸들링이 등록되어 있더라도 현재 핸들링 되고 있는 객체 그 이상 뿐만아니라 동  등한 레벨까지도 이벤트가 전파되지 않게 한다.




브라우저 호환성


브라우저마다 표준을 구현한 방식이 다르고 확장된 부분 또한 다르므로 브라우저 호환성이 필요한 자바스크립트 웹 애플리케이션을 개발해야 한다면 브라우저 호환성을 테스트하는 코드를 작성해야 한다.

먼저 해당 속성과 메서드가 존재하는지 체크하고 나서 현재 원하는 속성과 메서드를 브라우저에서 지원하는지 파악하여야 한다.


그러나 모두 이런식으로 호환성 테스트를 하면서 프로그램을 작성하기랑 여간 번거롭지 않다. 따라서 요즘은 브라우저의 객체 모델에 상관없이 통일된 인터페이스로 코드를 작성할 수 있게 만들어 주는 자바스크립트 라이브러리가 많이 있다.

jQuery도 그러한 라이브러리 가운데 하나이다.














참고.


자바스크립트 객체지향프로그래밍.

댓글