본문 바로가기
프론트엔드/JS 공부

이벤트 전파(Event propagation)에 대한 정리

by 학습하는 청년 2024. 4. 5.

최종 수정 : 2024-04-05

필요한 지식

  • DOM 트리
  • 이벤트
  • 이벤트 객체

추후 링크 연결 예정


이벤트 전파(evnet propagation)

말 그대로, DOM 요소 노드에서 발생한 이벤트가 DOM 트리를 통해 전파되는 것을 말한다.

이벤트 전파

이벤트가 발생하면, 이벤트 객체가 생성된다. 이벤트가 발생한 DOM 요소인 이벤트 타깃(event target)을 중심으로 DOM 트리를 통해 이벤트가 전파된다. 이벤트 객체가 전달되는 방향에 따라 3단계로 구분할 수 있다.

 

(1) 캡처링 단계(capturing phase) : 이벤트가 상위 요소에서 하위 요소 방향으로 전파

(2) 타깃 단계(target phase) : 이벤트가 이벤트 타깃에 도달

(3) 버블링 단계(bubbling phase) : 이벤트가 하위 요소에서 상위 요소 방향으로 전파

 

이벤트가 등록된 방식에 따라 이벤트 캐치 단계가 다르다.

  • 이벤트 핸들러 어트리뷰트/프로퍼티 방식 : 타깃 단계, 버블링 단계
  • addEventListener 메서드 방식 : 메서드의 3번째 인수로 true를 전달하면, 캡처링 단계의 이벤트도 캐치할 수 있다.
<!DOCTYPE html>
<html>
<body>
  <ul id="language">
    <li id="js">JavaScript</li>
    <li id="ts">TypeScript</li>
    <li id="css">Tailwind CSS</li>
  </ul>
  <script>
    const $language = document.getElementById('language');
    const $js = document.getElementById('js');
    
    // #language 요소의 하위 요소인 li 요소를 클릭했을 떄, 캡처링 단계의 이벤트를 캐치
    $language.addEventListener('click', e => {
      console.log(`이벤트 단계: ${e.eventPhase}`); // 1: 캡처링 단계
      console.log(`이벤트 타깃: ${e.target}`); // [object HTMLLIElement]
      console.log(`이벤트 타깃: ${e.currentTarget}`); // [object HTMLUListElement]
    }, true);
    
    // 타깃 단계의 이벤트를 캐치
    $js.addEventListener('click', e => {
      console.log(`이벤트 단계: ${e.eventPhase}`); // 2: 타깃 단계
      console.log(`이벤트 타깃: ${e.target}`); // [object HTMLLIElement]
      console.log(`이벤트 타깃: ${e.currentTarget}`); // [object HTMLLIElement]
    });
    
    // 버블링 단계의 이벤트를 캐치
    $js.addEventListener('click', e => {
      console.log(`이벤트 단계: ${e.eventPhase}`); // 3: 버블링 단계
      console.log(`이벤트 타깃: ${e.target}`); // [object HTMLLIElement]
      console.log(`이벤트 타깃: ${e.currentTarget}`); // [object HTMLUListElement]
    });
  </script>
</body>
</html>

이처럼 이벤트는 이벤트 타깃은 물론 상위 DOM 요소에서도 캐치할 수 있다.

 

대부분의 이벤트는 캡처링과 버블링을 통해 전파된다. 하지만 아래의 이벤트는 버블링을 통해 전파되지 않는다. 아래 이벤트를 상위 요소에서 캐치해야 할 경우는 그리 많지 않다. 만약 캐치해야 한다면, 대체할 수 있는 이벤트가 존재한다.

  • 포커스 : focus / blur ==> 대체할 수 있는 이벤트 : focusin / focusout
  • 리소스 : load / unload / abort / error
  • 마우스 : moseenter / mouseleave ==> 대체할 수 있는 이벤트 : mouseover / mouseout

 

캡처링 단계의 이벤트와 버블링 단계의 이벤트를 캐치하는 이벤트 핸들러가 혼용되는 경우

<!DOCTPYE html>
<html>
<head>
  <style>
    html, body { height: 100% };
  </style>
<body>
  <p>버블링과 캡처링 이벤트 <button>버튼</button></p>
  <script>
    // 버블링 단계의 이벤트를 캐치
    document.body.addEventListener('click', () => {
      console.log('Handler for body.');
    });
    
    // 캡처링 단계의 이벤트를 캐치
    document.querySelector('p').addEventListener('click', () => {
      console.log('Handler for paragraph.');
    }, true);
    
    // 타깃 단계의 이벤트를 캐치
    document.querySelector('button').addEventListener('click', () => {
      console.log('Handler for button.');
    });
  </script>
</body>
</html>

button 클릭시

Handler for paragraph.
Handler for button.
Handler for body.

 

p 요소 클릭시

Handler for paragraph.
Handler for body.

결과에서 알 수 있듯이, 부모 노드들은 버블링 단계에서 호출된다. 타겟이 먼저 호출되고 나중에 부모 노드들의 이벤트 리스너들이 호출된다.


참고 자료

모던 자바스크립트 Deep Dive (p.779-783)

초보자를 위한 JavaScript 200제 (p.344-346)

 

https://ko.javascript.info/bubbling-and-capturing

댓글