Capturing & Bubbling

Capturing 과 Bubbling

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      .outer {
        width: 500px;
        height: 500px;
        background-color: yellow;
      }

      .middle {
        width: 50%;
        height: 50%;
        margin: auto;
        background-color: thistle;
        transform: translateY(50%);
      }

      button {
        position: relative;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
      }
    </style>
  </head>
  <body>
    <div class="outer">
      <div class="middle">
        <button>Click Me</button>
      </div>
    </div>
    <script>
      const outer = document.querySelector('.outer');
      const middle = document.querySelector('.middle');
      const button = document.querySelector('button');

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

상기 코드에서 버튼을 클릭하면, 부모의 이벤트까지 모두 호출되는걸 확인할 수 있다.

이는 bubbling up이 되는 것이다.

버블링을 막는 방법 ? 🚫

button.addEventListener('click', event => {
        console.log(`button1 ${event.currentTarget}, ${event.target}`);
        event.stopPropagation(); // 위로 버블링이 일어나지않게 처리
      });

위와 같이 버블링이 일어나지않도록 처리할 수 있다. 하지만, 이렇게 처리할경우 여전히 button1, button2가 출력된다.

button.addEventListener('click', event => {
        console.log(`button1 ${event.currentTarget}, ${event.target}`);
        event.stopImmediatePropagation(); // 실제로 이벤트가 일어난 엘리멘트만 핸들링
      });

event.stopImmediatePropagation() 로 실제로 이벤트가 일어난 엘리멘트만 핸들링이 가능하다.

하지만 버블링을 stopPropagation() 로 처리하는것은 좋지 않다 (위험하다)

예를 들어, 부모 컨테이너에서 자식 이벤트가 발생할때마다 중요한 로직이 발생한다고 할때 자식에서만 처리하고 stopPropagation() 한다는 것은 정말 위험하다.

이상적인 방법 ✅

부모에서 막는 방법이 좋다.

이벤트에 있는 타겟이 일어난 위치와 이벤트에 있는 현 타겟이 다르면 이벤트를 발생시키지 않는 방식으로 처리

      const outer = document.querySelector('.outer');
      const middle = document.querySelector('.middle');
      const button = document.querySelector('button');

      outer.addEventListener('click', event => {
        if (event.target !== event.currentTarget) {
          return;
        } // 이벤트가 발생한곳과 현재 이벤트타겟이 다를때 실행취소
        console.log(`outer: ${event.currentTarget}, ${event.target}`);
      });
      middle.addEventListener('click', event => {
        if (event.target !== event.currentTarget) {
          return;
        }
        console.log(`middle ${event.currentTarget}, ${event.target}`);
      });
      button.addEventListener('click', event => {
        console.log(`button1 ${event.currentTarget}, ${event.target}`);
      });
      button.addEventListener('click', event => {
        console.log(`button2 ${event.currentTarget}, ${event.target}`);
      });