프론트엔드 기본개념 복습/Javascript

[Javascript] 이벤트 버블링, 이벤트 캡쳐링 (Event Bubbling, Event Capturing)

콘요맘떼 2022. 2. 24. 22:03

이벤트 버블링 (Event Bubbling)

  한 아이템에 이벤트가 발생하면 해당 이벤트는 해당 아이템부터 하나씩 그 부모 요소로, 결국은 body element까지 전달된다. 만약 해당 이벤트에 대해서 부모 element 역시 이벤트 핸들러가 존재한다면 하나의 이벤트에 대해서 여러가지 요소들에 대한 이벤트 핸들러가 동작할 수 있다. 이렇게 이벤트가 가장 깊은 곳의 element부터 부모 element로 쭉쭉 거슬러올라가는 방식을 이벤트 버블링이라고 한다. 참고로 모든 이벤트가 버블링되는 것은 아니며 focus 이벤트와 같이 버블링이 적용되지 않는 이벤트들도 존재한다. (물론 거의 모든 이벤트는 버블링이 적용된다.)

 

  위의 예시에서 '아무말'이라는 텍스트를 클릭하면 click이라는 이벤트가 span, div, form 모두에게 전달된다. 세 element 모두 click에 대한 이벤트 핸들러를 가지고 있기 때문에 콘솔을 확인하면 'SPAN', 'DIV', 'FORM'이 차례대로 출력되는 것을 확인할 수 있을 것이다.

event.target vs this

- event.target : 이벤트가 발생한 가장 안쪽의 element

- this (=event.currentTarget) : 해당 이벤트 핸들러가 실행중인 element

→ 이벤트 버블링 과정에서 거치는 모든 element들에 대해서 event.target은 동일하게 적용되지만 this는 다르게 적용된다. (위의 예시에서 event.target은 span이다.)

 

event.stopPropagation( )

  이벤트 버블링을 의도적으로 중단하고 싶다면 이벤트 핸들러 안에서 event 객체의 event.stopPropagation 메소드를 실행하면 된다. event.stopPropagation( )는 특정 이벤트에 대해서만 이벤트 버블링을 중단해줄 수 있기 때문에 특정 element에 대한 모든 이벤트 버블링을 중단하고 싶다면 모든 이벤트 핸들러에 해당 메소드를 적용하거나 event.stopImmediatePropagation( ) 메소드를 활용하는 방법이 있다.

  기본적으로 버블링을 막는 것은 권장되는 태도가 아니다. 예를 들어서 사용자 분석을 위해 window 객체에 click event 핸들러를 추가하였는데 하위 element의 이벤트 핸들러에서 stopPropagation( )을 실행한다면 window 객체에서는 제대로 분석을 실행할 수 없다.

 

 

이벤트 캡처링 (Event Capturing)

  이벤트의 흐름에는 이벤트 버블링을 포함하여 총 세 가지의 흐름이 존재한다.

 

1) 이벤트 단계 : 이벤트가 상위 요소에서 하위 요소로 전파되는 단계

2) 타겟 단계 : 이벤트가 가장 안쪽에 있는 실제 타겟 요소로 전달되는 단계

3) 버블링 단계 : 이벤트가 타겟 요소에서 다시 상위 요소로 전파되는 단계

 

이미지 출처 : https://javascript.info/bubbling-and-capturing

  위 이미지는 파란색으로 색칠될 td element에 대해서 이벤트가 실행되었을 때 발생하는 이벤트 흐름이다. 우선적으로 최상위 element인 window부터 실제 타겟인 td까지 내려가는 과정이 캡쳐링 단계이다. 이어서 실제 타겟인 td에 이벤트가 도달하는 타겟 단계가 일어나고 다시 최상위 element까지 이벤트가 전파되는 버블링 단계가 일어나게 된다. 실제로 이벤트 핸들러가 발생하는 단계는 캡쳐링 단계와 버블링 단계 뿐이다. 타겟 단계에서는 이벤트 핸들러가 별도로 동작하지 않는다.

  캡쳐링 단계를 활용하는 경우는 흔치 않다. 그렇기 때문에 이벤트 핸들러의 디폴트 설정 역시 캡쳐링 단계가 아니라 버블링 단계를 기준으로 설정되어 있다. 만약 이벤트 핸들러가 캡쳐링 단계를 기준으로 트리거되길 원한다면 addEventListener 함수의 세 번째 파라미터를 수정해주면 된다.

 

  addEventListener 함수의 세 번째 파라미터는 optional parameter로 options 객체 혹은 캡쳐링 단계를 기준으로 할 것인지를 지정하는 boolean 타입의 useCapture을 넘겨줄 수 있다. 따라서 캡쳐링에 대해서 이벤트 핸들러를 만들어주고 싶다면 위 이미지의 마지막 두 가지 방법 중 하나를 활용하면 된다. 이벤트 캡쳐링은 이벤트 버블링과 정반대의 방향을 따른다. 그렇기 때문에 해당 포스트의 맨 처음에서 제시했던 예시의 모든 이벤트 핸들러를 캡쳐링 기준으로 설정하였다면 콘솔에는 정 반대의 결과인 'FORM', 'DIV', 'SPAN'이 출력되었을 것이다. 참고로 하나의 element에 여러 이벤트 핸들러가 적용될 수 있듯이 element에게 특정 event에 대한 캡쳐링 기준 이벤트 핸들러와 버블링 기준 이벤트 핸들러를 모두 추가해줄 수 있다. 또한 캡쳐링 기준의 핸들러와 버블링 기준 핸들러는 서로 다른 핸들러이기 때문에 만약 addEventListener(..., ..., true)로 핸들러를 추가해줬다면 삭제 역시 removeEventListener(..., ..., true)와 같은 방식을 이용해야 한다.

 

 

  이벤트 버블링과 이벤트 캡쳐링에 대한 학습이 끝났다면 이벤트 위임을 다뤄볼 수 있다. 하지만 글이 너무 길어져서 이벤트 위임은 다음 포스트에서 따로 정리해야겠다.