이벤트 캡처링과 버블링 그리고 위임

19108 단어 JavaScriptJavaScript

이벤트

  • 이벤트(event)웹 브라우저가 알려주는 HTML 요소에 대한 사건의 발생을 의미한다.
  • 웹 페이지에 사용된 자바스크립트는 이렇게 발생한 이벤트에 반응하여 특정 동작을 수행할 수 있다.

이벤트 핸들러

  • 이벤트에 반응하려면 이벤트가 발생했을 때 실행되는 함수핸들러(handler)를 할당해야 한다.
  • 핸들러사용자의 행동에 어떻게 반응할지를 자바스크립트 코드로 표현한 것입니다.
  • addEventListener() 메소드를 사용하여 이벤트 핸들러를 등록한다.
  • removeEventListener() 메소드를 사용하여 이벤트 핸들러를 삭제한다.

이벤트 버블링

  • 한 요소에 이벤트가 발생하면, 이 요소에 할당된 핸들러가 동작하고, 이어서 부모 요소의 핸들러가 존재한다면 동작한다. 가장 최상단의 조상 요소를 만날 때까지 이 과정이 반복되면서 요소 각각에 할당된 핸들러가 동작한다.
      const outer = document.querySelector('.outer');
      const middle = document.querySelector('.middle');
      const button = document.querySelector('button');

      outer.addEventListener('click', (e) => {
        console.log(`${e.currentTarget}, ${e.target}`);
      });

      middle.addEventListener('click', (e) => {
        console.log(`${e.currentTarget}, ${e.target}`);
      });

      button.addEventListener('click', (e) => {
        console.log(`${e.currentTarget}, ${e.target}`);
      });
  • 위 코드에서 부모 자식 순서는 outer > middle > button 이고 로그의 출력순서는 button, middle, outer순으로 버블링이 이루어지는 것을 볼 수 있다.

이벤트 버블링 중단하기

  • 핸들러에게 이벤트를 완전히 처리하고 난 후 버블링을 중단하도록 명령할 수도 있다.
  • 이벤트 객체의 메서드인 event.stopPropagation()event.stopImmediatePropagation()를 사용한다.

    event.stopPropagation()

    위쪽으로 일어나는 버블링은 막아주지만, 같은 요소에 핸들러가 여러개가 존재한다면 다른 핸들러들의 동작을 막지 못한다.

    event.stopImmediatePropagation()

    같은 요소의 특정 이벤트에서 메소드를 실행한 핸들러만 동작하고 나머지 핸들러 모두가 동작하지 않는다.

  • 버블링을 꼭 멈춰야 하는 명백한 상황이 아니라면 버블링을 막지말고, 아키텍처를 잘 고려해 진짜 막아야 하는 상황에서만 버블링을 막아야 한다.
      const outer = document.querySelector('.outer');
      const middle = document.querySelector('.middle');
      const button = document.querySelector('button');

      outer.addEventListener('click', (e) => {
        // 메소들을 사용한 버블링 막기보다 안정적인 방법
        // 이벤트가 발생한 타겟과 현재 핸들러가 동작하는 타겟이 같은 경우가 아니면 아무동작없이 return
        if (e.currentTarget !== e.target) {
          return;
        }

        console.log(`${e.currentTarget}, ${e.target}`);
      });

      middle.addEventListener('click', (e) => {
        // 메소들을 사용한 버블링 막기보다 안정적인 방법
        // 이벤트가 발생한 타겟과 현재 핸들러가 동작하는 타겟이 같은 경우가 아니면 아무동작없이 return
        if (e.currentTarget !== e.target) {
          return;
        }

        console.log(`${e.currentTarget}, ${e.target}`);
      });

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

이벤트 캡처링

  • 이벤트 흐름엔 3가지 단계가 존재한다.
    • 캡처링 단계 : 이벤트가 하위 요소로 전파되는 단계
    • 타깃 단계 : 이벤트가 실제 타깃 요소에 전달되는 단계
    • 버블링 단계 : 이벤트가 상위 요소로 전파되는 단계

  • 위 그림은 td를 클릭하면 이벤트가 최상위 조상에서 시작해 아래로 전파되고(캡처링 단계), 이벤트가 타깃 요소에 도착해 실행된 후(타깃 단계), 다시 위로 전파됩니다(버블링 단계). 이런 과정을 통해 요소에 할당된 이벤트 핸들러가 호출됩니다.
  • 캡처링 단계를 이용해야 하는 경우는 흔치 않기 때문에, 버블링 단계에서의 로직 처리가 더 자주 사용된다.

이벤트 위임

  • 이벤트 위임비슷한 방식으로 여러 요소를 다뤄야 할 때 사용한다.
  • 이벤트 위임을 사용하면 요소마다 핸들러를 할당하지 않고, 요소의 공통 조상에 이벤트 핸들러를 단 하나만 할당해도 여러 요소를 한꺼번에 다룰 수 있습니다.
      // Bad Code
      // 모든 자식요소에 이벤트리스너를 등록하여 메모리 낭비
      const lis = document.querySelectorAll('li');

      lis.forEach((el) => {
        el.addEventListener('click', () => {
          el.classList.add('selected');
        });
      });

      // Good Code
      // 이벤트 위임을 통한 강력한 핸들링
      const ul = document.querySelector('ul');

      ul.addEventListener('click', (e) => {
        // e.target을 통해 이벤트가 발생한 요소를 알 수 있다.
        if (e.target.tagName === 'LI') {
          e.target.classList.add('selected');
        }
      });


참고자료

좋은 웹페이지 즐겨찾기