브라우저에서 발생한 이벤트가 DOM 트리를 따라 전달되는 이벤트 전파 과정을 설명하는 개념
프론트엔드는 사용자의 다양한 액션을 처리해야한다.
이때 이벤트가 단순히 특정 요소에서만 발생하는 것이 아니라,
DOM 트리를 따라 전파되기 때문에 이벤트가 어떻게 전달되는지를 이해하는 것이 중요하다.
이벤트 전파 구조를 이해하고 잘 활용한다면,
등을 효율적으로 구현할 수 있다.
브라우저의 이벤트 감지 방법├── 이벤트 버블링├── 이벤트 캡처링└── 이벤트 위임├── stopPropagation()└── stopImmediatePropagation()브라우저 이벤트 시스템├── 이벤트 전파(Event Propagation)│ ├── 이벤트 캡처링│ ├── 이벤트 타겟│ └── 이벤트 버블링├── 이벤트 위임(Event Delegation)│ ├── event.target│ ├── closest()│ └── matches()└── 이벤트 제어(Event Control)├── stopPropagation()├── stopImmediatePropagation()
HTML 요소들은 트리 구조로 계층적으로 이루어져 있다.
특정 요소에서 이벤트가 발생하면, 브라우저는 단순히 그 요소만 실행하고 끝내는 것이 아니라 계층을 따라 이벤트를 연쇄적으로 전달한다.
이러한 현상들을 이벤트 전파(Event Propagation) 라고 한다.
이때 전파하는 방향이 어느 쪽이냐에 따라 크게 2가지로 나누어진다.
표준 DOM 이벤트 모델은 이 전파 과정을 총 3단계로 정의하고 있다.
| 단계 | 명칭 | 설명 |
| 1단계 | 캡처링 단계 | 이벤트가 window에서 시작해 타깃 요소까지 내려가는 과정 |
| 2단계 | 타깃 단계 | 이벤트가 실제 타깃 요소(e.target)에 도착해 핸들러가 호출되는 과정 |
| 3단계 | 버블링 단계 | 이벤트가 다시 부모 요소를 타고 최상위 객체까지 올라가는 과정 |
아래 사진은 td에서 클릭이 이루어졌을 때 이벤트의 흐름이다.

간단하게 이러한
키워드가 있다라고 생각하고, 좀 더 자세하게 알아보자!
아래 예시를 보면 div에 이벤트 리스너가 할당되어있다.
하지만 실제로 브라우저에서의 동작을 보면 내부에 있는 em, code 등을 클릭해도 동일한 이벤트가 실행된다.
<div onclick="alert('div에 할당한 핸들러!')"><em><code>EM</code>을 클릭했는데도 <code>DIV</code>에 할당한 핸들러가 동작합니다.</em></div>
이렇게 자식 요소에서 발생한 이벤트가 부모 요소로 전파되는 것을 버블링이라고 한다.
1. 한 요소에서 이벤트가 발생하면, 특정 요소에 할당된 핸들러가 동작한다.2. 이후, 부모 요소의 핸들러가 동작한다.3. 위의 과정을 최상단에 있는 조상 요소를 만날 때까지 반복한다.-> 결국, 요소 각각에 할당된 핸들러가 모두 동작하게 된다.
Event 객체에 있는 함수들을 통해 버블링을 방지할 수 있다.
가장 많이 사용되는 stopPropagation() 와 stopImmediatePropagation() 함수를 간단하게 알아보자.
둘 다 간단하게 말하면 버블링을 방지하는 함수이다. 그럼 이 두 가지의 차이는 무엇일까?
두 함수의 가장 큰 차이는 범위이다.
위에서 나온 버블링이 모든 이벤트에 기본으로 적용되는 것은 아니다.
아래에 큰 단위로 버블링이 되지 않는 이벤트와 어떤 논리흐름인지를 간단하게 정리하였다.
이 부분은 암기하려고 하기보단 이러한 논리로 버블링이 되지 않는 구나의 참고자료로 이해하고 넘어가도 좋을 것 같다.
→ 특정 요소 내에서 끝내도록 함
| 카테고리 | 이벤트 이름 | 설명 | 버블링 여부 |
| 포커스 | focus, blur | 요소가 포커스를 얻거나 잃을 때 | X (대신 focusin, focusout은 됨) |
| 마우스 | mouseenter, mouseleave | 마우스가 요소 경계 안팎으로 이동할 때 | X (대신 mouseover, mouseout은 됨) |
| 리소스/미디어 | load, unload |
캡처링은 한 요소에 이벤트가 발생됐을 때, 해당 요소의 자손 이벤트도 같이 발생되는 방식이다.
이벤트 캡처는 기본적으로 비활성화되어있으며, 활성화하기 위해서는 옵션을 추가해줘야한다.
3번째 인자에 객체로 캡처를 활성화시키거나, 그냥 true 값을 넘겨도 동일하게 작동한다.
document.body.addEventListener("click", handleClick, { capture: true });document.body.addEventListener("click", handleClick, true );
const output = document.querySelector("#output");function handleClick(e) {output.textContent += `${e.currentTarget.tagName} 요소를 클릭했습니다.\n`;
위의 예시가 있다면, 메시지 순서는 버블링과 반대이다.
<body> 이벤트 처리기 → <div> 이벤트 처리기 →<button> 이벤트 처리기
BODY 요소를 클릭했습니다.DIV 요소를 클릭했습니다.BUTTON 요소를 클릭했습니다.
브라우저에서 기본으로 제공하는 이벤트에 대한 값들이 있다.
그 중 대표적인 event.target 와 event.currentTarget 의 차이를 간단하게 알고가자.
아래 코드에서 this 는 이벤트 리스너가 바인딩된 요소(parent)를 가리킴.
// <div id="parent"><button id="child">클릭</button></div> 이 있고,// 부모인 div에 리스너를 달았다고 가정document.getElementById('parent').addEventListener('click', function(e) {console.log(e.target.id); // "child" (실제 클릭된 버튼)
** 여기서 주의할 것은 화살표 함수를 사용한다면 상위 스코프를 가져오게 됨.
만약 this 를 사용하고자 했다면 화살표함수가 아닌 일반 함수를 활용할 것.
(보통은 e.currentTarget 사용을 더 권장함)
document.getElementById('parent').addEventListener('click', (e) => {console.log(this); // window 객체 (혹은 undefined)!console
이벤트 위임은 이벤트 버블링을 활용한 패턴이다.
여러 개의 자식 요소에 각각 이벤트를 등록하는 대신,
부모 요소 하나에만 이벤트를 등록하고 자식 이벤트를 처리하는 방식이다.
<ul id="list"><li>1</li><li>2</li><li>3</li></ul>
document.getElementById("list").addEventListener("click", (e) => {if (e.target.tagName === "LI") {console.log(e.

참고
| 이미지/스크립트 로딩 완료, 에러 발생 시 |
| X |
| UI/기타 | scroll, resize | 요소가 스크롤되거나 창 크기가 바뀔 때 | X (window 객체 제외) |