JavaScript

웹 개발의 필수 언어

동적인 웹 페이지 구현을 위한 핵심 프로그래밍 언어.

Java

객체지향 프로그래밍

안정적이고 확장성 있는 백엔드 개발의 대표 언어.

HTML

웹의 기초

웹 페이지의 구조를 정의하는 마크업 언어.

React

현대적 UI 라이브러리

효율적인 사용자 인터페이스 구축을 위한 JavaScript 라이브러리.

CSS

웹 디자인의 핵심

웹 페이지의 시각적 표현을 담당하는 스타일 언어.

Spring

자바 웹 프레임워크

기업급 애플리케이션 개발을 위한 강력한 프레임워크.

JavaScript/자바 스크립트 기초

이벤트 버블링 (Event Bubbling)과 캡처링 (Event Capturing)

lamarcK 2025. 3. 21. 23:47

이벤트 버블링은 웹 개발에서 이벤트가 발생하는 방식 중 하나이다. 특정 요소에서 이벤트가 발생했을 때, 그 이벤트가 해당 요소의 상위 요소들로 차례대로 전달되는 현상을 말한다. 마치 물방울이 아래에서 위로 올라가는 모습과 비슷하다고 하여 '버블링(bubbling)'이라는 이름이 붙었다.

이벤트 버블링의 작동 방식

  1. 이벤트 발생: 가장 안쪽 요소에서 이벤트가 발생한다. 예를 들어, 버튼을 클릭하면 버튼 요소에서 클릭 이벤트가 발생한다.
  2. 이벤트 전파: 발생한 이벤트는 해당 요소의 부모 요소로 전달됩니다. 부모 요소에도 동일한 이벤트에 대한 이벤트 핸들러가 있다면 실행됩니다.
    • 핸들러 있음: 핸들러를 실행하고 다음 상위 요소로 이벤트를 전달합니다.
    • 들러 없음: 아무런 반응 없이 다음 상위 요소로 이벤트를 전달합니다.
  3. 상위 요소로 전달: 이벤트는 계속해서 상위 요소로 전달되며, 각 요소에 등록된 이벤트 핸들러가 있는지 확인하고 실행합니다. 
  4. 최상위 요소까지 전파: 이벤트는 문서의 최상위 요소인 document 객체에 도달할 때까지 전파됩니다.

이벤트 버블링은 겹쳐있기 때문에 발생하는 것이 아니다.

 

예를 들면 이렇게 회색 박스(부모 요소) 안에 분홍 박스(자식 요소)를 추가 한다고 치자. 저 클릭하세요 부분을 눌렀을 때 눌리는 좌표는 부모요소가 혼자 있을 때랑 시각적으로 동일한 위치가 눌릴 것이다. 때문에 자식 요소부모 요소가 같이 눌리게 되는 것처럼 착각 할 수 있다. 하지만 실제로 클릭이 되는 부분은 시각적으로 가장 앞쪽에 있는 자식 요소뿐이라서 부모 요소는 같이 눌리지 않는다.

때문에 이벤트 버블링이 이벤트를 자식에서 부모로 전파하는 이유는 부모와 자식 요소가 겹쳐있는 것과는 전혀 다른 이유다.

 

실제 클릭 대상 확인 순서

더보기
  • 레이어(layer) 순서 확인
    • 브라우저는 HTML 요소들을 **쌓인 순서(레이어 개념)**로 인식합니다.
    • 시각적으로 가장 위에 있는 요소가 클릭됩니다.
  • CSS 속성 적용
    • z-index 값이 높은 요소가 위로 올라가므로, 더 높은 z-index를 가진 요소가 우선 클릭됩니다.
    • position: absolute | fixed | relative; 같은 속성이 있으면 z-index를 통해 순서를 조정할 수 있습니다.
  • 요소의 영역(Visibility & Opacity) 고려
    • display: none; → 화면에 없으므로 클릭되지 않음
    • visibility: hidden; → 보이지는 않지만 공간은 차지하므로 여전히 클릭 가능
    • opacity: 0; → 투명하지만 여전히 클릭 가능
    • pointer-events: none; → 이 요소는 완전히 클릭되지 않음 (클릭 이벤트 무시됨)
  • HTML 계층(DOM 구조)에서 가장 깊은 요소부터 확인
    • 부모 요소와 자식 요소가 겹쳐 있다면, 브라우저는 DOM 트리에서 가장 깊은 요소(자식)를 먼저 확인합니다.
    • 부모보다 자식이 먼저 클릭되지만, 이벤트 버블링이 발생하면 부모까지 클릭된 것처럼 보일 수 있음.

 

이벤트 버블링은 프로그램의 자연스러운 현상이 아니라 필요에 의해 만들어진 것이다.

초기 웹 개발에서는 이벤트를 개별적으로 처리해야 했다. 즉, 버튼을 클릭하면 버튼에만 이벤트가 걸려 있고, 부모 요소는 이 이벤트를 알지 못했기 때문에 웹 개발이 굉장히 불편하고, 유지보수가 어려웠다. 서로 연동이 되지 않았기에 코드를 각각 작성해 줘야 했다.

 

비유하자면 팔이 다쳤는데 신호(이벤트)가 부모 요소인 몸에 전달이 되지 않아 다친것도 모르는 식이다.

 

이를 개선하기 위해 이벤트 흐름(Event Flow) 개념이 생겼고,
1998년 W3C(월드 와이드 웹 컨소시엄)이 공식적으로 이벤트 흐름을 표준화했다.

이때 두 가지 이벤트 흐름을 정의했다.

  1. 캡처링 (Event Capturing) → 부모에서 자식으로 전달
  2. 버블링 (Event Bubbling) → 자식에서 부모로 전달

대부분의 브라우저(특히 Internet Explorer)는 버블링을 기본값으로 채택했는데 이는 이벤트 위임(Event Delegation)을 쉽게 구현할 수 있도록 하기 위해서였다.

 

여기서 이벤트 캡처링은 정확히 이벤트 버블링과 반대 순서로 작용하는 것이다. 부모 요소에 이벤트가 발생하면 그것이 자식 단계 까지 차례대로 내려오는 것이다.

 

본래 이벤트 캡처링과 버블링은 항상 함께 일어난다.

즉, 위에서 내려오는 캡처링과 아래에서 위로 올라가는 버블링은 반드시 일어나게 되어있다. 하지만 동시에 발생하는 것은 불가능하기 때문에 프로그램은 캡처링과 버블링을 순차적으로 실행한다.

실행 순서는 캡처링 → 타깃 → 버블링 순이다.

 

  • 캡처링 단계(Capture Phase):
    • 이벤트가 최상위 요소(window 객체)에서 시작하여 이벤트가 발생한 요소까지 하위 요소로 전파된다.
  • 타깃 단계(Target Phase):
    • 이벤트가 실제 발생한 요소에 도달한다.
  • 버블링 단계(Bubbling Phase):
    • 이벤트가 발생한 요소에서 시작하여 최상위 요소까지 상위 요소로 전파된다.

 

이벤트 흐름 순서는 다음과 같다.

  1. 캡처링 단계:
    • window → document → html → body → <부모1> → <자식1> → <손자1>
  2. 타깃 단계:
    • <손자1>
  3. 버블링 단계:
    • <손자1> → <자식1> → <부모1> → body → html → document → window

단 캡처링과 버블링 모두 '동일한 이벤트 발생 조건을 가진 요소'라면 해당 요소에 등록된 이벤트를 발동시킨다. 하지만 무슨 이벤트를 하나 발동시킬 때마다 윈도우 단계에서 부터 이벤트가 거꾸로 내려오면 답도 없이 때문에 기본적으로 캡처링은 발동하지 않도록 되어있다.

캡처링을 사용해서 이벤트 흐름을 확인하고 싶다면 element.addEventListener('event_type', callback, true) 같이 캡처링 부분을 true로 설정해야한다.


이벤트 버블링 예시

<div id="parent">
  <button id="child">클릭!</button>
</div>

<script>
  document.getElementById('parent').addEventListener('click', function(event) {
    console.log('부모 div 클릭!');
  });

  document.getElementById('child').addEventListener('click', function(event) {
    console.log('자식 버튼 클릭!');
  });
</script>

위 코드에서 버튼을 클릭하면 "자식 버튼 클릭!"과 "부모 div 클릭!"이 차례대로 콘솔에 출력된다. 이는 이벤트 버블링 때문에 자식 요소의 클릭 이벤트가 부모 요소로 전달되었기 때문이다.

이벤트 버블링 예시2

더보기
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>이벤트 버블링 예제</title>
</head>
<body>
    <div id="parent" style="background-color: gray; height: 100px; width: 100px;">
        <button id="child">클릭하세요</button>
    </div>
    <script src="script.js"></script>
</body>
</html>
function handleClick(event) {
    alert(`${event.currentTarget.id}가 클릭되었습니다.`);
}

document.addEventListener("DOMContentLoaded", function () {
    const parent = document.getElementById("parent");
    const child = document.getElementById("child");

    parent.addEventListener("click", handleClick); // 부모 요소에 이벤트 리스너 추가
    child.addEventListener("click", handleClick); // 자식 요소에도 추가 (버블링 발생)
});

 

이처럼 자식요소인 버튼을 클릭하면 이벤트 버블링에 의해서 상위 요소도 동일한 이벤트 핸들러가 발동하는 것을 볼 수 있고 반대로 부모요소만 클릭할 경우 부모요소만 발동한다.