[JavaScript] 이벤트 버블링(Bubbling) & 캡처링(Capturing)

haha-rumi·2022년 3월 29일
0

JavaScript

목록 보기
2/6

Event를 공부하면서 버블링(Bubbling)과 캡처링(Capturing)에 대한 개념이 나와서 정리를 해보기로 했다.

캡처링(Capturing)

window로부터 이벤트가 발생한 요소까지 이벤트를 전파한다. 즉, 이벤트가 하위 요소로 전파되는 단계다. 실제 코드에서 자주 쓰이지는 않지만 개념적으로만 알고있으면 될 것 같다.

버블링(Bubbling)

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

버블링 현상 예

FORM > DIV > P 형태로 중첩된 구조가 있고, 요소 각각에 핸들러가 할당되어 있다.

<style>
  body * {
    margin: 10px;
    border: 1px solid blue;
  }
</style>

<form onclick="alert('form')">FORM
  <div onclick="alert('div')">DIV
    <p onclick="alert('p')">P</p>
  </div>
</form>

가장 안쪽의 <p>를 클릭하면 순서대로 다음과 현상이 나타난다.

  1. <p>에 할당된 onclick 핸들러가 동작.
  2. 바깥의 <div>에 할당된 핸들러가 동작.
  3. 그 바깥의 <form>에 할당된 핸들러가 동작.

document 객체를 만날 때까지, 각 요소에 할당된 onclick 핸들러가 동작한다.

거의 모든 이벤트는 버블링이 된다.

여기서 키워드는 '거의'이다. Focus 이벤트와 같은 버블링이 되지 않는 이벤트도 있다.

event.target

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

event.currentTarget과 event.target

  • event.target은 실제 이벤트가 시작된 '타깃'요소이다. 버블링이 진행되어도 변하지 않는다.
  • event.currentTarget은 현재 요소로 현재 실행 중인 핸들러가 할당된 요소를 참조한다.
<body>
  <div class="outer">
      <button>Click Me</button>
  </div>
  <script>
    const outer = document.querySelector('.outer');![]
    const button = document.querySelector('button');

    outer.addEventListener('click', event => {
      console.log(`outer: ${event.currentTarget}, ${event.target}`);
    });
    button.addEventListener('click', event => {
      console.log(`button1 ${event.currentTarget}, ${event.target}`);
    });
  </script>
</body>

위에 코드에서 button을 클릭하면 console에 아래와 같이 나온다.

여기서 event.target 즉, 이벤트가 시작된 타깃요소는 HTMLButtonElement가 되고, event.currentTarget.outer의 HTMLDivElement와 HTMLButtonElement가 나타나게 된다.

버블링 중단

기본적으로 알고 있는 방법이 이벤트 객체의 메서드인 event.stopPropagation()를 사용하는 방법이다.

event.stopPropagation()

위에 코드의 button에 event.stopPropagation()를 추가하게 되면 button에서만 핸들링이 일어나고 버블링이 일어나지 않는다.

button.addEventListener('click', event => {
      console.log(`button1 ${event.currentTarget}, ${event.target}`);
  	  event.stopPropagation()
    });

event.stopImmediatePropagation()

event.stopPropagation()은 위쪽으로 일어나는 버블링은 막아주지만, 다른 핸들러들이 동작하는 건 막지 못 한다.

    button.addEventListener('click', event => {
      console.log(`button ${event.currentTarget}, ${event.target}`);
      event.stopPropagation()
    });
    button.addEventListener('click', event => {
      console.log(`button2 ${event.currentTarget}, ${event.target}`);
    });

위에 코드처럼 같은 button에 적용된 핸들러가 있을 경우, event.stopPropagation()만으로는 동작을 멈추지 못 한다. 이때 사용되는 것이 event.stopImmediatePropagation()다. 이 메서드를 사용하면 버블링을 멈추고, 요소에 할당된 다른 핸들러의 동작도 막아준다.

하지만!! event.stopPropagation()와 event.stopImmediatePropagation()의 사용은 위험하다.

내가 마음대로 이벤트를 취소해버리면 다른 부분에서 이 이벤트를 통해 더 의미 있는 일을 할 수도 있고 다른 기능을 추가해놨을 수도 있기 때문이다. 큰규모의 프로젝트 등에서 이것으로 인한 오류가 발행 할 수도 있다. 따라서 조건문을 이용하여 버블링을 막아주는 방법을 사용하면 좋다.

조건문을 이용한 버블링 중단

    outer.addEventListener('click', event => {
      if (event.target !== event.currentTarget) {
        return;
      }
    });

위에 코드처럼 event.target와 event.currentTarget을 이용하여 조건문을 만들어주면 된다.

참고사이트 및 강의

모던 JavaScript 튜토리얼

드림코딩엘리 프론트엔드 필수 브라우저 101 강의

profile
개발자 한 발

0개의 댓글