DOM 요소는 계층 구조를 이루므로, 버튼에서 발생한 이벤트를 상위 요소에서도 처리할 수 있습니다. 캡처링과 버블링은 이벤트가 이 계층을 따라 전달되는 방향을 구분합니다.
이벤트 전파 단계
- 캡처링: 이벤트 경로의 상위 요소에서 대상 방향으로 내려갑니다.
- 타깃: 이벤트가 발생한 대상에 도달합니다.
- 버블링: 대상에서 상위 요소 방향으로 올라갑니다.
addEventListener는 기본적으로 캡처링 리스너를 등록하지 않습니다. { capture: true } 옵션을 주면 캡처링 단계에서 처리합니다. focus처럼 버블링하지 않는 이벤트도 있으므로 모든 이벤트가 동일하게 올라오는 것은 아닙니다.
실행 순서 확인
다음 내용을 하나의 HTML 문서에서 실행하고 버튼을 누르면 순서를 확인할 수 있습니다.
<div id="panel">
<button id="save" type="button">저장</button>
</div>
<script>
const panel = document.getElementById("panel");
const save = document.getElementById("save");
const steps = [];
panel.addEventListener("click", () => {
steps.length = 0;
steps.push("부모 캡처링");
}, { capture: true });
save.addEventListener("click", () => steps.push("버튼 타깃"));
panel.addEventListener("click", () => {
steps.push("부모 버블링");
console.log(steps);
console.assert(
steps.join(",") === "부모 캡처링,버튼 타깃,부모 버블링"
);
});
</script>
target과 currentTarget
event.target: 이벤트의 원래 대상입니다.event.currentTarget: 현재 실행 중인 리스너가 등록된 요소입니다.
부모 리스너에서 버튼 클릭을 처리하면 target은 버튼이고 currentTarget은 부모입니다. 버튼 안의 아이콘을 클릭하면 target은 아이콘일 수 있다는 점도 고려해야 합니다.
이벤트 위임
목록의 각 버튼에 리스너를 붙이는 대신 공통 부모에 리스너 하나를 등록하고 클릭한 항목을 판별하는 방식입니다. 나중에 추가되는 자식 요소도 부모를 통해 처리할 수 있습니다.
대상을 찾을 때는 closest 등으로 원하는 버튼을 찾고, 해당 버튼이 처리할 부모 영역 안에 있는지도 확인합니다. 중첩된 목록을 사용할 때 특히 필요합니다.
전파와 기본 동작은 별개
stopPropagation()은 이벤트 전파를 멈추고, preventDefault()는 링크 이동이나 폼 제출 같은 기본 동작을 막습니다. 하나가 다른 하나까지 대신하지는 않습니다. 전파 차단을 남용하면 부모의 이벤트 위임이나 분석 로직이 동작하지 않을 수 있습니다.
참고