
- 캡처링 단계 – 이벤트가 하위 요소로 전파되는 단계
- 타깃 단계 – 이벤트가 실제 타깃 요소에 전달되는 단계
- 버블링 단계 – 이벤트가 상위 요소로 전파되는 단계
예시
테이블 안의 <td>를 클릭하면 어떻게 이벤트가 흐르는가?

→ <td>를 클릭하면 이벤트가 최상위 조상에서 시작해 아래로 전파되고(캡처링 단계), 이벤트가 타깃 요소에 도착해 실행된 후(타깃 단계), 다시 위로 전파됩니다(버블링 단계).
한 요소에 이벤트가 발생 → 이 요소에 할당된 핸들러가 동작 → 부모 요소의 핸들러가 동작함.
가장 최상단의 조상 요소(몇 이벤트는 window 객체 그 외에는 document 객체)를 만날 때까지 이 과정이 반복되면서 요소 각각에 할당된 핸들러가 동작함.
‘거의’ 모든 이벤트는 버블링 됨. (버블링 되지 않는 이벤트도(focus 이벤트 등) 존재)
<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>

https://run.plnkr.co/preview/cl6d6z5zz00033b70hmog5bgg/
동작 원리
가장 안쪽의 <p>를 클릭 시
<p>에 할당된 onclick 핸들러가 동작.
바깥의 <div>에 할당된 핸들러가 동작.
그 바깥의 <form>에 할당된 핸들러가 동작.
document 객체를 만날 때까지, 각 요소에 할당된 onclick 핸들러가 동작.
동작 결과
<p> 요소 클릭 시, p → div → form 순서로 3개의 alert 창 출현.
타깃(target) 요소
이벤트가 발생한 가장 안쪽의 요소
event.target으로 접근 가능부모 요소의 핸들러는 자세한 정보 얻기 가능.
(이벤트가 정확히 어디서 발생했는지 등)
| event.target | this (=event.currentTarget) | |
|---|---|---|
| 설명 | 실제 이벤트가 시작된 ‘타깃’ 요소 | ‘현재’ 요소 |
| 특징 | 버블링이 진행되어도 안 변함. | 현재 실행 중인 핸들러가 할당된 요소를 참조 |
예시
상황
핸들러는 form.onclick 하나밖에 없지만 이 핸들러에서 폼 안의 모든 요소에서 발생하는 클릭 이벤트를 ‘잡아내고(catch)’ 있음.
이유
클릭 이벤트가 어디서 발생했든 상관없이 <form> 요소까지 이벤트가 버블링 되어 핸들러를 실행시키기 때문

form.onclick 핸들러 내의 this와 event.target
| this (event.currentTarget) | event.target | |
|---|---|---|
| 가리키는 요소 | 요소 (이유: 요소에 있는 핸들러가 동작해서) | 폼 안쪽에 실제 클릭한 요소 |
<form> 요소 클릭 시
event.target === this
event.stopPropagation()활용
핸들러에게 이벤트를 완전히 처리한 뒤, 버블링을 중단하도록 명령 가능
예시
<button>을 클릭해도 body.onclick은 동작 x

꼭 필요한 경우를 제외하곤 버블링을 막지 말 것
버블링은 유용함.
버블링을 꼭 멈춰야 하는 명백한 상황이 아니라면 버블링을 막지 말 것.
대안 有 (커스텀 이벤트 등을 사용)
자세한 이유 참고 : 링크
event.stopImmediatePropagation()
버블링을 멈추고, 요소에 할당된 다른 핸들러의 동작도 막으려면 사용
이 메서드를 사용하면 요소에 할당된 특정 이벤트를 처리하는 핸들러 모두가 동작 x
한 요소의 특정 이벤트를 처리하는 핸들러가 여러 개인 상황에서, 핸들러 중 하나가 버블링을 멈추더라도 나머지 핸들러는 여전히 동작
event.stopPropagation()은 위쪽으로 일어나는 버블링은 막아주지만, 다른 핸들러들이 동작하는 건 못 막음.
캡처링 단계를 이용해야 하는 경우는 흔치 x
on<event>프로퍼티, HTML 속성, addEventListener(event, handler)를 이용해 할당된 핸들러
→ 캡처링에 대해 전혀 알 수 x
2, 3 번째 단계의 이벤트 흐름(타깃 단계와 버블링 단계)에서만 동작
캡처링 단계에서 이벤트를 잡아내려면
addEventListener의 capture 옵션을 true로 설정
elem.addEventListener(..., {capture: true})
// 아니면, 아래 같이 {capture: true} 대신, true를 써줘도 됩니다.
elem.addEventListener(..., true)
capture 옵션
__이면 핸들러는 __ 단계에서 동작.
false→ 버블링true→ 캡처링
이벤트가 실제 타깃 요소에 전달되는 단계인 ‘타깃 단계’(두 번째 단계)는 별도로 처리되지 x.
캡처링과 버블링 단계의 핸들러는 타깃 단계에서 트리거 됨.
문서 내 요소 '전체’에 핸들러를 할당해서 어떤 핸들러가 동작하는지

<p> 클릭 시, 이벤트 전달 순서
HTML → BODY → FORM → DIV (캡처링 단계, 첫 번째 리스너)
P (타깃 단계, 캡쳐링과 버블링 둘 다에 리스너를 설정했기 때문에 두 번 호출됩니다.)
DIV → FORM → BODY → HTML (버블링 단계, 두 번째 리스너)
event.eventPhase
현재 발생 중인 이벤트 흐름의 단계 파악 가능
방법
반환되는 정숫값에 따라 이벤트 흐름의 현재 실행 단계를 구분
이 프로퍼티는 자주 사용 되지 x.
이유: 핸들러를 통해 흐름 단계 파악 가능
핸들러를 제거 시, removeEventListener가 같은 단계에 있어야 함.
addEventListener(..., true)로 핸들러를 할당해 줬다면, 핸들러를 지울 때, removeEventListener(..., true)를 사용해 지워야 함.
같은 단계에 있어야 핸들러가 지워집니다.
같은 요소와 같은 단계에 설정한 리스너는 설정한 순서대로 동작
특정 요소에 addEventListener를 사용해 한 단계에 이벤트 핸들러를 여러 개 설정했다면 이 핸들러들은 설정한 순서대로 동작
elem.addEventListener("click", e => alert(1)); // 첫 번째로 트리거됨.
elem.addEventListener("click", e => alert(2));
e.preventDefault()
참고