사용자의 액션에 의해 다이나믹한 화면을 구성함에 있어 Javascript의 이벤트는 가장 중요한 키워드가 아닐까 싶습니다. 이벤트에서 버블링(Bubbling)과 캡쳐링(Capturing)이 무엇이고 어떻게 동작하는지 알아보겠습니다
표준 DOM 이벤트에서 정의한 3가지 이벤트 흐름
1. Capturing 단계 - 이벤트가 하위 요소로 전파되는 단계
2. Target 단계 - 이벤트가 실제 타깃 요소로 전달되는 단계
3. Bubbling 단계 - 이벤트가 상위 요소로 전파되는 단계
예시코드)
<div onclick="alert('div')"> // div
<form onclick="alert('form')"> // form
<button onclick="alert('button')"> // button
button
</button>
</div>
</div>
button을 클릭했을 때 다음과 같은 일이 발생
위와 같은 흐름을 이벤트 버블링(Bubbling)이라고 함
Javascript에서 동작하는 거의 모든 이벤트에서는 버블링이 일어남 - focus, blur등 몇몇 이벤트 제외
부모 요소의 핸들러는 이벤트가 정확하게 어디서 발생되었는지에 대한 정보를 얻을 수 있다 이벤트가 발생한 가장 안쪽의 요소는 타킷(target) 요소 라고 하며, event.target을 사용하여 접근할 수 있다.
<div onclick="alert('div')"> // div
<form> // form
<button> // button
button
</button>
</div>
</div>
각 요소를 클릭했을 때 target과 currentTarget
핸들러는 div(최상단)에 있지만 핸들러에서 div 안의 모든 요소에서 발생하는 클릭 이벤트를 catch하고 있기때문에, 이벤트가 어디서 발생하든 div까지 이벤트가 버블링 되어 핸들러를 실행시킴
<div onclick="alert('이벤트가 여기까지 도달하지 못함')">
<button onclick="event.stopPropagation()">클릭!!</button>
</div>
이벤트 객체 메서드인 stopPropagation()을 사용하면 버블링을 중단 할 수 있음
element.addEventListener('', { capturing: true });
element.addEventListener('', true);
addEventListener의 두번째 인자로 코드와 같이 넣어주면 Capturing을 catch할 수 있음
두번째 인자의 default는 false이고 bubbling이 동작
공식적인 이벤트 흐름은 3단계지만 이벤트가 실제 타깃 요소에 전달되는 단계인 타깃 단계는 별도로 처리되지 않음 Capturing과 Bubbling단계의 핸들러는 타깃 단계에서 트리거됨
<form>FORM
<div>DIV
<p>P</p>
</div>
</form>
<script>
for(let elem of document.querySelectorAll('*')) {
elem.addEventListener("click", e => alert(`캡쳐링: ${elem.tagName}`), true);
elem.addEventListener("click", e => alert(`버블링: ${elem.tagName}`));
}
</script>
문서 내 요소 전체에 대한 핸들러가 동작하는 방법
P를 클릭했을 때 이벤트 흐름
1. HTML > BODY > FORM > DIV (Capturing 단계)
2. P (Target 단계 / Capturing과 Bubbling에서 리스너를 설정했기 때문에 두번 호출)
3. DIV > FORM > BODY > HTML (Bubbling 단계)
핸들러를 제거할 때도 addEventListener에 설정한 값 그대로 removeEventListener에 설정해야 핸들러가 정상적으로 제거 됨
ex)
addEventListener(..., true) > removeEventListener(..., true)
참고