캡처링
이벤트가 발생했을 때 최상위 요소(window)에서 시작하여 실제 타겟 요소까지 아래로 내려가는 전파 단계입니다.
전체 이벤트 흐름에서의 위치
이벤트 전파 3단계:
① 캡처링 (Capturing) ←── 위에서 아래로
② 타겟 (Target) ←── 실제 클릭한 요소
③ 버블링 (Bubbling) ←── 아래에서 위로
① 캡처링 ③ 버블링
│ ▲
window ────────┼────────────────────────────┼───
│ │
document ──────┼────────────────────────────┼───
│ │
html ──────────┼────────────────────────────┼───
│ │
body ──────────┼────────────────────────────┼───
│ │
div ───────────┼────────────────────────────┼───
│ │
▼ │
button ────────────── ② 타겟 ───────────────┘
캡처링 vs 버블링
| 항목 | 캡처링 | 버블링 |
|---|---|---|
| 방향 | 위 → 아래 | 아래 → 위 |
| 순서 | 먼저 발생 | 나중에 발생 |
| 기본값 | X (명시 필요) | O (기본) |
| 비유 | 잠수 | 부상 |
비유:
캡처링 = 돌을 물에 던짐 (위에서 아래로)
수면 → 물속 깊이 → 바닥(타겟)
버블링 = 바닥에서 공기방울 올라옴 (아래에서 위로)
바닥(타겟) → 물속 → 수면
캡처링 사용 방법
// 기본값: 버블링 단계에서 실행
element.addEventListener("click", handler); // 버블링
element.addEventListener("click", handler, false); // 버블링
// 캡처링 단계에서 실행
element.addEventListener("click", handler, true); // 캡처링
element.addEventListener("click", handler, { capture: true }); // 캡처링
동작 확인 예시
<div id="outer">
<div id="inner">
<button id="btn">클릭</button>
</div>
</div>
버블링만 사용 (기본)
outer.addEventListener("click", () => console.log("outer"));
inner.addEventListener("click", () => console.log("inner"));
btn.addEventListener("click", () => console.log("btn"));
버튼 클릭 시:
btn ← 타겟
inner ← 버블링 (아래→위)
outer ← 버블링
캡처링만 사용
outer.addEventListener("click", () => console.log("outer"), true);
inner.addEventListener("click", () => console.log("inner"), true);
btn.addEventListener("click", () => console.log("btn"), true);
버튼 클릭 시:
outer ← 캡처링 (위→아래)
inner ← 캡처링
btn ← 타겟
캡처링 + 버블링 함께
outer.addEventListener("click", () => console.log("1. outer 캡처"), true);
inner.addEventListener("click", () => console.log("2. inner 캡처"), true);
btn.addEventListener("click", () => console.log("3. btn 타겟"));
inner.addEventListener("click", () => console.log("4. inner 버블"));
outer.addEventListener("click", () => console.log("5. outer 버블"));
버튼 클릭 시:
1. outer 캡처 ← 캡처링 (위→아래)
2. inner 캡처
3. btn 타겟 ← 타겟 단계
4. inner 버블 ← 버블링 (아래→위)
5. outer 버블
캡처링 타겟 버블링
outer ──(1)───→ ←──(5)──
inner ──(2)───→ ←──(4)──
btn ────(3)────
캡처링이 필요한 경우
대부분의 경우 버블링만 사용하지만, 특수한 상황에서 캡처링이 유용합니다.
1. 이벤트를 자식보다 먼저 가로채기
// 특정 조건에서 자식 이벤트를 차단
outer.addEventListener("click", (e) => {
if (shouldBlock) {
e.stopPropagation(); // 여기서 중단 → 자식에게 도달 안 함
console.log("차단됨");
}
}, true); // 캡처링이므로 자식보다 먼저 실행
btn.addEventListener("click", () => {
console.log("버튼 클릭"); // shouldBlock이면 실행 안 됨
});
shouldBlock = true일 때:
outer 캡처 실행 → stopPropagation() → 전파 중단
→ btn 핸들러 실행 안 됨
캡처링 단계에서 막았기 때문에 타겟까지 안 내려감
2. 전역 이벤트 감지 (모든 클릭 로깅)
// 모든 클릭을 가장 먼저 감지
document.addEventListener("click", (e) => {
console.log("클릭 발생:", e.target);
// 분석 데이터 전송 등
}, true);
// 이벤트는 계속 전파됨 (stopPropagation 안 했으므로)
3. focus/blur 이벤트 위임
// focus는 버블링 안 됨 → 캡처링으로 위임 구현
form.addEventListener("focus", (e) => {
e.target.classList.add("focused");
}, true); // 캡처링으로 잡아야 함
form.addEventListener("blur", (e) => {
e.target.classList.remove("focused");
}, true);
// 또는 focusin/focusout 사용 (버블링 됨)
form.addEventListener("focusin", handler); // 버블링 O
캡처링 단계에서 stopPropagation()
outer.addEventListener("click", (e) => {
console.log("outer 캡처");
e.stopPropagation(); // 캡처링 단계에서 중단
}, true);
inner.addEventListener("click", () => console.log("inner 캡처"), true);
btn.addEventListener("click", () => console.log("btn"));
inner.addEventListener("click", () => console.log("inner 버블"));
outer.addEventListener("click", () => console.log("outer 버블"));
버튼 클릭 시:
outer 캡처 ← 여기서 stopPropagation()
← 이후 전부 실행 안 됨
캡처링에서 막으면:
- 타겟까지 도달 안 함
- 버블링도 안 일어남
- 사실상 이벤트가 사라짐
stopPropagation()
│
outer (캡처) ────────X ← 여기서 중단
inner (캡처) │
btn (타겟) │ 모두 실행 안 됨
inner (버블) │
outer (버블) ▼
캡처링 vs 버블링 선택 기준
일반적인 이벤트 처리 → 버블링 (기본)
캡처링이 필요한 경우:
├── 자식 이벤트를 부모가 먼저 가로채야 할 때
├── 전역 이벤트 모니터링 (로깅, 분석)
├── focus/blur 이벤트 위임
└── 특정 조건에서 이벤트 차단
실무에서는 99%가 버블링 사용
캡처링은 특수한 경우에만 사용
핵심 정리
캡처링은 이벤트 전파의 첫 번째 단계로, window에서 타겟까지 위에서 아래로 내려가는 과정입니다. addEventListener의 세 번째 인자로 true 또는 { capture: true }를 전달하면 캡처링 단계에서 핸들러가 실행됩니다. 일반적으로 버블링이 기본이며 대부분의 상황에서 충분하지만, 자식보다 먼저 이벤트를 가로채거나, 버블링되지 않는 이벤트를 위임할 때 캡처링이 필요합니다.