버블링의 원리는 간단하다.
한 요소에 이벤트가 발생하면, 이 요소에 할당된 핸들러가 동작하고, 이어서 부모 요소의 핸들러가 동작한다. 가장 최상단의 조상 요소를 만날 때까지 이 과정이 반복되면서 요소 각각에 할당된 핸들러가 동작한다.
예시를 보자. (3개의 요소가 FORM > DIV > P 형태로 중첩된 구조)
<style>
body * {
margin: 10px;
border: 1px solid blue;
}
</style>
<form onclick="alert('form')">FORM
<div onclick="alert('div')">DIV
<p onclick="alert('p')">P</p>
</div>
</form>
가장 안쪽의 <p>
를 클릭하면 순서대로 다음과 같은 일이 발생한다.
1. <p>
에 할당된 onclick
핸들러가 동작한다.
2. 바깥의 <div>
에 할당된 핸들러가 동작한다.
3. 그 바깥의 <form>
에 핧당된 핸들러가 동작한다.
4. document
객체를 만날 때까지, 각 요소에 할당된 onclick
핸들러가 동작한다.
이런 동작 방식 때문에 <p>
요소를 클릭하면 p
-> div
-> form
순서로 3개의 alert 창이 뜨는것이다.
이런 흐름을 이벤트 버블링이라고 부른다.
📌 거의 모든 이벤트는 버블링 된다.
focus
이벤트와 같이 버블링 되지 않는 이벤트도 있다.
이벤트 버블링은 event.stopPropagation()
를 사용하면 중단시킬 수 있다.
아래 예시에서 <buttn>
을 클릭해도 body.onclick
은 동작하지 않는다.
<body onclick="alert(`버블링은 여기까지 도달하지 못합니다.`)">
<button onclick="event.stopPropagation()">클릭해 주세요.</button>
</body>
📌 event.stopImmediatePropagation()
한 요소의 특정 이벤트를 처리하는 핸들러가 여러개인 상황에서, 핸들러 중 하나가 버블링을 멈추더라도 나머지 핸들러는 여전히 동작한다.
event.stopPropagation()
은 위쪽으로 일어나는 버블링은 막아주지만, 다른 핸들러들이 동작하는건 막지 못한다.
버블링을 멈추고, 요소에 할당된 다른 핸들러의 동작도 막으려면event.stopImmediatePropagation()
를 사용해야 한다. 이 메서드를 사용하면 요소에 할당된 특정 이벤트를 처리하는 핸들러 모두가 동작하지 않는다.
⚠ 꼭 필요한 경우를 제외하면 버블링을 막지 말 것
버블링을 꼭 멈춰야 하는 명백한 상황이 아니라면 버블링을 막지 말자. 아키텍처를 잘 고려해 진짜 막아야 하는 상황에서만 버블링을 막을 것
이벤트 캡처링이라는 흐름은 실제 코드에서 자주 쓰이진 않지만, 종종 유용한 경우가 있다.
표준 DOM 이벤트에서 정의한 이벤트 흐름에는 3가지 단계가 있다.