카테고리 없음

Event Propagation

LucetTin5 2023. 1. 23. 23:07

이번 글은 발생된 이벤트의 전파에 대한 글이다.

https://developer.mozilla.org/en-US/docs/Web/API/Event

 

Event - Web APIs | MDN

The Event interface represents an event which takes place in the DOM.

developer.mozilla.org

이벤트는 사용자의 액션에 의해 발생할 수도 있고, API에 의해 생성될 수도 있고, 요소를 선택한 후 click 메소드를 호출하는 방식 혹은 dispatchEvent를 통해 발생될 수도 있다.

특정 상황에서 발생하기를 원하는 이벤트는 onEvent와 같은 형식으로 DOM Node에 부착되거나 addEventListener를 통해 Event발생 시 Event Listener를 작동시키는 방식으로 추가될 수 있다. 이벤트 리스너는 자동적으로 event를 parameter로 받는다. 이는 이벤트 객체이며 이벤트 발생 시 자동으로 이벤트리스너에 전달되는 매개변수이다.

 

브라우저 상에서 이벤트를 다루는 데 있어 중요한 개념은 이벤트의 전파이다.

먼저 이벤트의 버블링과 캡처에 대해 알아보려 한다. 

 

부모 요소를 갖고 있는 요소에서 이벤트가 발생될 시, 현대 브라우저들은 캡처링 단계와 버블링 단계를 실행하게 되어있다.

현대 브라우저들은 이벤트 핸들러가 기본적으로 버블링 단계에 등록되어 있으므로 캡처링보다는 버블링 단계를 통해 이벤트 전파가 일어나게 된다. (이벤트 핸들러가 캡처링 단계로 진행되게 하려면 addEventListener를 작성 시 세 번째 parameter로, 옵셔널한 parameter인 useCapture를 true로 지정해주면 된다)

 

캡처링 단계에서는 요소의 가장 먼 조상( <html> )에게, 캡처링 단계에 대하여 등록된 이벤트 핸들러가 있는지를 확인하고 있다면 실행한다. 그리고 해당 요소 내부의 다음 요소로 이동하며 같은 것을 반복하고, 선택된 요소에 닿을 때까지 반복한다.

버블링 단계에서는 반대로 선택된 요소가 버블링 단계에 대하여 등록된 이벤트 핸들러가 있는지를 확인하고 있다면 실행한다. 그리고 다음 조상 요소로 이동하여 진행하고 <html> 요소에 닿을 때까지 계속 진행한다.

https://developer.mozilla.org/ko/docs/Learn/JavaScript/Building_blocks/Events#event_bubbling_and_capture

위 이미지는 이벤트 버블링과 캡처링 단계를 도식화한 그림이다. 버블링 단계를 통한 전파에서는 <video>가 클릭 시 onclick이 발생하고 조상의 <div>의 onclick이, 다음 조상들(<html>에 닿을 때까지)의 onclick이 일어난다. 캡처링 단계를 통한 이벤트 전파에서는 <html>에서부터 onclick 이벤트를 찾아 있다면 실행하고 <div>의 onclick이, 마지막으로 <video>의 onclick 이벤트가 실행되게 된다.

 

위와 같은 이벤트의 전파는 원하지 않는 이벤트의 발생을 야기할 수도 있다. 이를 방지하는 방법이 있다. 표준 이벤트 객체(Event)는 stopPropagation이라는 메소드를 가지고 있다. 이는 핸들러의 이벤트 객체가 호출될 시, 이벤트가 더 이상 상위로 전파되지 않도록 하여 원치않는 이벤트의 전파를 막도록 할 수 있다.

 

이벤트의 위임(Event delegation) > 상위 요소에 의한 하위 요소들의 이벤트 제어

버블링을 이용하면 이벤트 위임을 가능하게 한다. 예를 들어 여러 자식 요소를 갖는 어느 요소가 있다고 하자. 이 중 하나를 선택했을 때 발생하는 이벤트가 있다면 일반적으로는 모든 자식 요소에 이벤트 리스너를 설정해야 한다. 하지만, 버블링을 생각해보자. 버블링은 자식 요소에서 발생한 이벤트가 상위로 전파되는 것이다. 이벤트 리스너가 모든 자식을 공통으로 갖는 부모 요소에 지정되어 있다면, 자식 요소에서 발생한 이벤트는 부모 요소까지 버블링되고 하나의 이벤트 리스너를 통해 자식 요소들에 이벤트를 부여할 수 있게 된다.

 

Event의 속성 중...

- Event.bubbles: 읽기 전용의 속성으로 이는 이벤트가 DOM Tree를 타고 위쪽으로 버블링 되는지를 의미하는 Boolean값이다. 

- Event.eventPhase: 읽기 전용의 속성으로 현재 이벤트의 흐름 단계를 정수값으로 나타낸다.
  - 0(NONE): 이벤트가 처리중이 아님을 나타낸다
  - 1(CAPTURING_PAHSE): 이벤트가 부모 객체를 통해 전파중임을 의미한다. Window > Document > <html> 을 거쳐 대상의 부모에 도달할 때까지 지속된다.

  - 2(AT_TARGET): 이벤트가 대상에 도착했음을 의미한다. 위의 Event.bubbles가 false일 시 Event.AT_TARGET 단계 종료와 함께 이벤트 처리도 종료된다.

  - 3(BUBBLING_PHASE): 이벤트가 대상의 조상을 따라 역순으로 전파중임을 의미한다. 대상의 부모로부터 <html>을 거쳐 Window에 도달할 때까지 지속된다. 이를 버블링이라고 부르며 위의 Event.bubbles가 true일 시에만 발생한다.

- Event.stopImmediatePropagation: 동일 이벤트에 대해 다른 리스너가 발동하지 못하도록 막는 메소드이다. 동일 이벤트 유형에 대해 여러 리스너를, 하나의 요소에 부착했을 시 리스너의 호출 순서는 부착 순서와 동일하다. 리스너들 중 하나에서 해당 메소드를 호출 시 이후의 리스너들은 호출되지 않는다.

- Event.stopPropagation: 현재의 이벤트가 캡처링/버블링 단계에서 더 이상 전파되지 않도록 방지한다. 단, 전파를 방지하더라도 이벤트의 기본 동작은 실행된다. 또한, 동일 이벤트 대상에 연결된 다른 리스너까지 막지는 않기에 해당 리스너까지 방지하려면 위의 stopImmediatePropagtion 메소드를 호출해야 한다.

+stopPropagation을 피해야 하는 이유

- 서로 간섭하는 이벤트 핸들러가 여럿 있는 경우에 전파의 중지로 인해 모든 이벤트가 발생되는 경우를 볼 수도 있다. 

- 예기치 않게 다른 이벤트 핸들러의 실행을 막는 경우를 발생시킬 수 있다. 이벤트의 기본 동작을 막기 위해서라면 preventDefault를 사용하여 해당 이벤트의 기본 동작만을 차단하는 방식을 사용하는 것이 보다 낫다. 또는 여러 이벤트들을 모두 위임하여 event.target을 이용한 이벤트 위임을 통해 컨트롤하는 편이 낫다.

 

https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation

 

Event: stopPropagation() method - Web APIs | MDN

The stopPropagation() method of the Event interface prevents further propagation of the current event in the capturing and bubbling phases. It does not, however, prevent any default behaviors from occurring; for instance, clicks on links are still processe

developer.mozilla.org