[JS]이벤트 버블링, 캡처링, 위임

김민영·2024년 1월 26일
0

이벤트 흐름

DOM 이벤트에서 정의한 이벤트 흐름에는 3가지 단계가 있다.

  1. 캡처링 단계 : 이벤트가 하위 요소로 전파되는 단계
  2. 타깃 단계 : 이벤트가 실제 타깃 요소에 전달되는 단계
  3. 버블링 단계 : 이벤트가 상위 요소로 전파되는 단계

이벤트가 발생하면 가장 먼저 window 객체에서부터 target 까지 이벤트 전파가 일어난다.(캡처링 단계)
그리고 나서 타깃에 도달하면 타깃에 등록된 이벤트 핸들러가 동작하고,(타깃 단계)
이후 다시 window 객체로 이벤트가 전파된다.(버블링 단계)

버블링

한 요소에 이벤트가 발생하면, 이 요소에 할당된 핸들러가 동작하고, 이어서 부모 요소의 핸들러가 동작한다. 가장 최상단의 조상 요소를 만날 때까지 이 과정이 반복되면서 요소 각각에 할당된 핸들러가 동작한다.

이벤트가 제일 깊은 곳에 있는 요소에서 시작해 부모 요소를 거슬러 올라가며 발생하는 모양이 마치 물 속 거품(bubble)과 닮았기 때문에 '이벤트 버블링(Event bubbling)' 이라고 부른다.

버블링 중단하기

이벤트 버블링은 타깃 이벤트에서 시작해서 <html> 요소를 거쳐 document 객체를 만날 때까지 각 노드에서 모두 발생한다. 몇몇 이벤트는 window 객체까지 거슬러 올라가기도 한다. 이 때도 모든 핸들러가 호출된다. 그런데 핸들러에게 이벤트를 완전히 처리하고 난 후 버블링을 중단하도록 명령할 수도 있다.

이벤트 객체의 메서드인 event.stopPropagation() 를 사용하면 된다. 하지만 이벤트 버블링을 막아야 하는 경우는 거의 없다. 버블링을 막아야 해결되는 문제라면 커스텀 이벤트 등을 사용해 문제를 해결할 수 있기 때문에 버블링을 꼭 멈춰야 하는 명백한 상황이 아니라면 버블링을 막지 않는 게 좋습니다.

이벤트 위임

버블링 개념을 활용하면 훨씬 효과적인 이벤트 관리를 할 수 있다. 예를 들어 자식 요소 각각에 이벤트 핸들러를 하나씩 등록할 필요 없이 부모 요소에서 한 번에 자식 요소들에 발생한 이벤트를 관리할 수도 있다.

이렇게 이벤트를 다루는 방식을 자식 요소의 이벤트를 부모 요소에 위임한다고 해서 이벤트 위임(Event Delegation) 이라고 부른다.

이벤트 위임을 잘 활용하면 훨씬 더 효과적으로 이벤트를 다룰 수 있다.

타깃 요소

부모 요소의 핸들러는 이벤트가 정확히 어디서 발생했는지 등에 대한 자세한 정보를 얻을 수 있다. 이벤트가 발생한 가장 안쪽의 요소는 타깃(target) 요소라고 불리고, event.target을 사용해 접근할 수 있다.

event.target과 this(=event.currentTarget)는 다음과 같은 차이점이 있다.

  • event.target은 실제 이벤트가 시작된 '타깃'요소이다. 버블링이 진행되어도 변하지 않는다.
  • this는 '현재'요소로, 현재 실행 중인 핸들러가 할당된 요소를 참조한다.

캡처링

이벤트가 발생하면 가장 먼저, 그리고 버블링의 반대 방향으로 진행되는 이벤트 전파 방식을 이벤트 캡처링(Event capturing) 이라고 한다.

캡처링 단계에서 이벤트를 발생시켜야 하는 일은 매우 드문 경우이다. 보통 타깃 단계에서 target에 등록된 이벤트 핸들러가 있으면 해당 이벤트 핸들러가 먼저 동작한 이 후에 버블링 단계에서 각 부모 요소에 등록된 이벤트 핸들러가 있으면 그 때 해당 이벤트 핸들러가 동작하는 것이 일반적이다.

하지만 캡처링 단계에서 이벤트 핸들러를 동작시킬 상황이 온다면, addEventListener에 세번째 프로퍼티에 true 또는 {capture:true}를 전달하면 된다.

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8">
    <title>minyoung</title>
    <link rel="stylesheet" href="style.css">
  </head>
  <body>
    <div>DIV
      <ul>UL
        <li>LI</li>
      </ul>
    </div>  
    <script>
      for (let elem of document.querySelectorAll('*')) {
        elem.addEventListener("click", e => alert(`캡쳐링 단계: ${elem.tagName}`), true);
        elem.addEventListener("click", e => alert(`버블링 단계: ${elem.tagName}`));
      }
    </script>
  </body>
</html>

참고

profile
으라차차

0개의 댓글

관련 채용 정보