이번에 프론트엔드 면접 기회를 얻게 되었는데, 기술 질문으로 이벤트 버블링과 캡처링에 대해 물어보았다
처음 들어보는 개념이었기 때문에 이번 기회를 삼아 공부해보려고 한다
먼저 예시를 살펴보자
<div onclick="alert('div 태그에 할당한 이벤트 핸들러')">
<span><code>span</code>태그를 클릭했을 때<code>div</code>태그는?</span>
</div>
이렇게 코드가 구성되어 있다면, 화면에 출력되는 텍스트를 클릭했을 때 alert 창이 뜨는 것을 확인할 수 있다.
프로젝트 했을 때에도 이런 식으로 버튼이나 링크 안에 다른 태그가 있도록 구성한 적이 있었다
분명히 div 태그에 이벤트가 설정되어 있는데 왜 span 태그의 항목을 클릭했을 때에도 이 이벤트가 실행되는 걸까?
원리는 아주 간단하다
한 요소에 이벤트가 발생하면, 그 요소에 할당된 이벤트가 실행되고 부모 요소에 할당된 이벤트가 실행된다
이 과정을 최상단의 요소로 올라갈 때까지 반복하는 것이 이벤트 버블링이다
거품이 일어나는 것처럼 보인다는 의미에서 버블링이라는 이름이 붙게 되었다
여기에 event.currentTarget에 대한 설명이 있는데 이 코드를 이용했다
위 링크에서 알 수 있듯이 클릭한 태그와 현재 동작하는 이벤트의 태그가 다르다는 것을 알 수 있다
event.target 을 사용하면 클릭한 태그의 정보를 알 수 있고, event.currentTarget 을 사용하면 현재 동작하는 이벤트의 태그를 알 수 있다
event.stopPropagation() 을 이용하면 버블링을 막을 수 있다
<div onclick="alert('여기까지는 버블링이 올라오지 않습니다')">
<div onclick="event.stopPropagation()">
<div onclick="alert('여기까지는 버블링이 올라옵니다')">
<button>클릭!</button>
</div>
</div>
</div>
이렇게 코드를 작성하고 버튼을 클릭하면 "여기까지는 버블링이 올라옵니다" 만 출력되는 것을 확인할 수 있다
이렇게 event.stopPropagaion() 을 이용하면 버블링에 의해 부모 요소까지 올라가는 것을 막을 수 있게 된다
하지만 일반적으로 버블링 동작을 막을 필요는 없다고 한다. 버블링을 막아야 한다면 커스텀 이벤트를 사용하는게 더 낫다고 한다
설계 단계에서 꼭 막아야 하는 상황이 발생하는 것을 제외하면 막지 않는 것이 좋을 것 같다
이벤트 캡처링은 버블링과 반대라고 생각하면 된다
부모 요소의 이벤트를 실행한 뒤 자식 요소의 이벤트를 실행하는 동작이라고 생각하면 된다
그림과 같은 방식으로 아래 요소에 이벤트가 전파되는 것처럼 동작하게 된다
즉, div 태그의 이벤트가 발생한 이후 span 태그의 이벤트가 발생하는 순서로 진행된다는 것이다
addEventListener() 의 capture 옵션을 true 로 설정했을 때 캡처링이 이루어지게 된다
그래서 캡처링 이벤트를 지우기 위해서
removeEventListener()를 사용하면, 마찬가지로 세 번째 인자로true를 넘겨줘야 한다
element.addEventListener('click', () => alert('캡처링'), true);
element.addEventListener('click', () => alert('캡처링'), {capture: true});
위 코드처럼 세 번째 인자로 true 를 넘겨주면 된다
이해를 위해서 박스 형태로 또 만들어 보았다
아래 박스 형태의 실습 코드는 여기에서 코드를 확인할 수 있습니다
element.addEventListener('click', (event) => alert(`캡처링 : ${element.tagName}`), true);
element.addEventListener('click', (event) => alert(`버블링 : ${element.tagName}`));
이렇게 구성했을 때 p 영역을 클릭하게 되면 6개의 알림이 뜬다
캡처링 : div > 캡처링 : span > 캡처링 : p > 버블링 : p > 버블링 : span > 버블링 : div 순으로 화면에 출력되는 것을 확인할 수 있다
이벤트 흐름에 의해서 이런 순서로 출력이 진행되는데, 캡처링 단계 > 타깃 단계 > 버블링 단계의 순서로 이루어지기 때문이다
현재 이벤트가 어떤 흐름에서 발생했는지 알 수 있게 해주는 것은 event.eventPhase 라는 프로퍼티를 이용하면 된다고 한다