캡처링

이벤트가 발생했을 때 최상위 요소(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 }를 전달하면 캡처링 단계에서 핸들러가 실행됩니다. 일반적으로 버블링이 기본이며 대부분의 상황에서 충분하지만, 자식보다 먼저 이벤트를 가로채거나, 버블링되지 않는 이벤트를 위임할 때 캡처링이 필요합니다.

CSLOG에서 보기