element.addEventListener(type, listener, options);
addEventListener의 세 번째 인자는 옵션이며, 아래 두 가지 형태로 쓸 수 있다.
브라우저에서 이벤트가 발생하면, 두 가지 방향으로 전파된다.
| 단계 | 설명 | 예시 |
|---|---|---|
| 캡처링 (capturing) | 이벤트가 최상위 요소(document)에서 시작해 타깃 요소까지 내려감 | 부모 → 자식 순서 |
| 버블링 (bubbling) | 이벤트가 타깃 요소에서 다시 부모로 올라감 | 자식 → 부모 순서 |
즉, 하나의 클릭이 발생해도
→ 캡처링 단계에서 부모들이 먼저 감지하고
→ 그다음에 타깃과 그 부모들이 버블링 단계에서 다시 감지한다.
이벤트를 버블링 단계에서 감지한다.
즉, 이벤트가 타깃까지 도달한 뒤 위로 올라올 때 실행된다.
element.addEventListener('click', handler, false);
이벤트를 캡처링 단계에서 감지한다.
즉, 이벤트가 타깃으로 내려가는 도중에 실행된다.
element.addEventListener('click', handler, true);
<div id="parent">
PARENT
<button id="child">CHILD</button>
</div>
<script>
const parent = document.querySelector('#parent');
const child = document.querySelector('#child');
parent.addEventListener('click', () => {
console.log('👨👩👧 parent clicked');
}, false); // 기본값: 버블링
child.addEventListener('click', () => {
console.log('🧒 child clicked');
}, true); // 캡처링
</script>
콘솔:
🧒 child clicked
👨👩👧 parent clicked
만약 반대로 parent의 세 번째 인자를 true로 하면
부모가 먼저 찍히고 자식이 나중에 찍힌다.
보통 이런 상황은 다음과 같은 경우다.
예를 들어,
child.addEventListener('click', e => e.stopPropagation());
parent.addEventListener('click', () => console.log('parent'), true);
이럴 땐 true를 넣은 부모 핸들러만 실행된다.
→ 이벤트 전파의 방향을 바꿔서 막힘을 우회한 것.
ES6 이후부터는 세 번째 인자 대신 객체를 넘길 수도 있다.
element.addEventListener('click', handler, {
capture: true,
once: true, // 한 번만 실행 후 자동 제거
passive: true, // preventDefault() 호출 불가 (스크롤 성능 향상)
});
가장 많이 쓰이는 건 capture, once, passive 세 가지.
| 옵션 | 설명 | 기본값 |
|---|---|---|
capture | true면 캡처링 단계에서 실행 | false |
once | 한 번 실행 후 자동 제거 | false |
passive | preventDefault() 비활성화 | false |
“addEventListener는 그냥 이벤트 등록하는 함수잖아?”
→ 맞아, 하지만 세 번째 인자 하나로 이벤트 흐름 전체가 바뀐다.