이벤트 버블링과 캡처링
브라우저는 특정 사건이 발생하면 이를 감지하여 이벤트를 트리거(trigger) 시킵니다. 이벤트가 발생됐을 때 실행할 함수를 이벤트 핸들러라고 하며 이벤트가 발생됐을 때 브라우저에게 핸들러 호출을 위임하는 것을 이벤트 핸들러 위임이라 합니다. 사용자가 함수를 언제 호출할지 모르므로 브라우저에게 함수 호출을 위임할 수 있습니다.
이벤트가 발생했을 때 DOM 요소 노드에서 발생된 이벤트는 DOM 트리를 타고 전파됩니다. 이를 이벤트 전파(Event Propagation)라 하는데 이벤트 전파 방향에 따라 단계를 구분합니다.
공식적으로는 총 3개의 흐름(하위 요소로 전달되는 캡쳐링 단계, 실제 타겟 요소에 전달되는 타겟 단계, 그리고 상위 요소로 전파되는 버블링 단계)이 있지만 타겟 단계는 별도로 처리되지 않습니다.
버블링 단계 (Bubbling Phase)
요소에서 이벤트가 발생하면 해당 요소의 이벤트 핸들러가 동작하고 순차적으로 부모 요소의 핸들러가 동작합니다. 이 흐름이 마치 물속 거품과 같다하여 이벤트 버블링이라고 부릅니다. <p>
태그를 클릭해도 순차적으로 p
> div
> form
이벤트 핸들러가 동작하게 됩니다.
e.stopPropagation()
을 통해 이벤트 전파를 막을 수 있습니다. 단 버블링 단계에서만 전파를 막을 수 있고 캡처링 단계는 막을 수 없습니다. <p>
태그를 클릭 시 stopPropagation
에 의해 상위 요소로 전파되지 않습니다. <div>
태그의 경우 상위로 전파되어 form 태그도 로그에 찍히는 걸 확인할 수 있습니다.
또한 아래의 이벤트는 버블링 되지 않습니다. 캡처링이나 대체 이벤트로 사용할 수 있습니다.
focus
->focusin
blur
->focusout
mouseEnter
->mouseover
mouseLeave
->mouseout
캡처링 단계 (Capturing Phase)
캡처링은 버블링의 반대 개념입니다. 하위 요소에서 이벤트가 발생했다면 상위 요소에서부터 순차적으로 이벤트가 실행됩니다. 캡처링 단계를 이용해야 하는 일이 흔지 않지만 캡처링을 사용하기 위해서는 자바스크립트에서는 elem.addEventListener(..., {capture: true})
또는 elem.addEventListener(..., true)
를, 리액트에서는 onClickCapture
를 사용하면 캡처링 단계에서 이벤트를 잡을 수 있습니다.
p
태그를 클릭했지만 콘솔에는 form > p > div
순서로 로그가 출력되는 이유는, form
태그의 이벤트 핸들러가 캡처링 단계에서 이벤트를 감지하고, p
와 div
태그의 이벤트 핸들러는 버블링 단계에서 이벤트를 감지하기 때문입니다.
이벤트 위임
자바스크립트는 기본적으로 버블링 단계에서 이벤트를 감지합니다. 이 흐름을 이용하여 모든 요소에 이벤트를 등록하지 않고 상위 요소에 이벤트를 등록하여 이벤트를 감지할 수 있습니다. 만약 100개의 요소에 동일한 이벤트를 등록해야 한다면 성능 저하의 및 유지 보수에도 좋지 않기에 이벤트 위임을 활용할 수 있습니다.
상위 요소에 onClick
이벤트를 위임 후 하위 요소를 클릭해도 이벤트가 실행됩니다. 이벤트 위임 시엔 currentTarget
대신 target
프로퍼티를 사용해야 합니다.
target
: 이벤트가 발생한 요소currentTarget
: 이벤트 핸들러가 등록된 요소
이벤트를 위임하지 않았다면 target
과 currentTarget
은 동일합니다.
e.preventDefault() 와 e.stopPropagation() 의 차이
stopPropagation
은 이벤트 전파를 막고
preventDefault
는 이벤트를 제외한 브라우저의 기본 기능(form submit, a 페이지 이동, checkbox 체크 등)을 막습니다.
참조
- 모던 자바스크립트 Deep Dive
- https://ko.javascript.info/bubbling-and-capturing