이벤트 버블링/캡쳐링, 이벤트 위임

미음제

·

2022. 10. 30. 12:27

이벤트 등록

 

출처 : https://poiemaweb.com/js-event

 

DOM API를 통해 DOM에 접근할 수 있다. 접근한 DOM 요소에 이벤트를 등록할 수 있는데 addEventListener를 활용해 이벤트를 등록할 수 있다.

 

이벤트 객체

 

addEventListener로 이벤트가 등록된 후, 이벤트가 발생하면 브라우저는 이벤트 객체를 생성한다. 해당 객체에는 이벤트가 발생한 요소와 대상에 대한 정보가 담겨 있다.

 

 

Event - Web API | MDN

Event 인터페이스는 DOM 내에 위치한 이벤트를 나타냅니다.

developer.mozilla.org

 

이벤트 객체 속성

 

  • event.target : 이벤트가 처음 발생한 대상
  • event.currentTarget : 이벤트의 현재 대상
  • event.type : 이벤트 타입
  • etc

 

 

 

Event - Web API | MDN

Event 인터페이스는 DOM 내에 위치한 이벤트를 나타냅니다.

developer.mozilla.org

 

이벤트 객체 메서드

 

  • event.preventDefault() : 취소 가능한 경우 이벤트 기본 동작을 취소
  • event.stopPropagation() : 이벤트 전파를 방지
  • etc

 

이벤트 흐름

 

https://www.w3.org/TR/DOM-Level-3-Events/#dom-event-architecture

 

HTML DOM Tree는 Tree 형태의 계층적 구조를 갖는다. 이벤트의 전파 흐름에 따라 버블링(Bubbling)캡쳐링(Capturing)으로 구분할 수 있다.

 

단, 버블링과 캡쳐링 동작은 독립된 동작이 아니라 반드시 캡쳐링 후 버블링 순서가 보장된다.

 

캡쳐링(Capturing)

 

DOM Tree의 상위 요소에서 하위 요소(이벤트가 발생한 타깃 요소)로 이벤트가 전파

 

타깃(Target)

 

event.currentTarget이 되는 요소

 

버블링(Bubbling)

 

DOM Tree 하위 요소(타깃이 되는 요소)에서 상위 요소로 이벤트가 전파. 대부분의 이벤트가 버블링이 되지만 focus와 같은 특정 이벤트는 버블링 되지 않는다.

 

이벤트 흐름을 나타내는 위 이미지에서 예를 들자면, <td/> 태그에서 event가 발생했을 경우 Window → Document → <html/><body/> <table/><tbody/><tr/><td/> 이벤트 타겟 순서로 캡처링이 진행된다.

 

<td/> 이벤트 타깃을 발견하고 다시 역순으로 Tree의 최상단으로 버블링이 발생한다.

 

버블링

 

// html
<body>
  <div class="one">
    <div class="two">
      <div class="three"></div>
    </div>
  </div>
  <script src="./1.js"></script>
</body>

// js
const $divs = document.querySelectorAll("div");
$divs.forEach(($div) => {
  $div.addEventListener("click", (e) => {
      console.log(e.currentTarget);
  });
});

 

중첩된 div 요소에서 최하단의 class가 'three'인 div 요소를 클릭하면 다음과 같은 결과가 나온다.

 

버블링

 

중첩된 요소의 최하단 요소(타깃)에서 상위로 이벤트가 버블링 된 것을 알 수 있다. 이벤트가 발생한 요소의 상위 요소, 즉 부모 요소에서부터 출력을 하고 싶다면 어떻게 할까? 이때 사용할 수 있는 것이 캡쳐링이다.

 

캡쳐링

 

addEventListener() 메서드의 3번째 매개변수(파라미터)는 Optional 한 값으로 default는 false이다. 3번째 매개변수에 해당하는 인자 값을 true로 지정해주면 캡처링을 활용할 수 있다. boolean 값 true를 그대로 전달하거나, {capture : true}를 전달하면 된다. 

 

// html
<body>
  <div class="one">
    <div class="two">
      <div class="three"></div>
    </div>
  </div>
  <script src="./1.js"></script>
</body>

// js
const $divs = document.querySelectorAll("div");
$divs.forEach(($div) => {
  $div.addEventListener(
    "click",
    (e) => {
      console.log(e.currentTarget);
    },
    true
  );
});

 

중첩된 div 요소에서 최하단의 class가 'three'인 div 요소를 클릭하면 다음과 같은 결과가 나온다.

 

캡쳐링

 

e.stopPropagation()

 

이벤트 객체 메서드인 e.stopPropagation()를 사용하면 현재 이벤트가 캡쳐링/버블링 단계에서 하위/상위 요소로 전파되지 않도록 막아준다.

 

e.stopPropagation() 메서드가 전파를 막지만 event 기본 동작은 실행된다. 예를 들어 link나 button의 클릭은 막지 못한다. 이 경우에는 e.preventDefault() 메서드를 활용해야 한다.

 

이벤트 위임(Delegation)

 

중첩된 여러 하위 요소가 존재할 때, 하위 요소 각각에 이벤트를 등록하지 않고 상위 요소에 이벤트를 등록한 후 하위 요소의 이벤트를 제어하는 것이다.

 

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <title>Documents</title>
  </head>
  <body>
    <ul class="list">
      <li>1</li>
      <li>2</li>
      <li>3</li>
      <li>4</li>
    </ul>
    <button class="add">Click</button>
    <script src="./1.js"></script>
  </body>
</html>

 

const $list = document.querySelector("ul");
const $listItem = document.querySelectorAll("li");
$listItem.forEach(($div) => {
  $div.addEventListener("click", (e) => {
    console.log(e.currentTarget.innerText);
  });
});

const $button = document.querySelector("button");
$button.addEventListener("click", () => {
  const $newItem = document.createElement("li");
  $newItem.innerText = "new Item";
  $list.appendChild($newItem);
});

 

기존에 생성되어 있던 <li /> 태그에 대해서는 이벤트가 정상 동작하지만, <button /> 클릭으로 새롭게 추가된 <li /> 태그에 대해서는 이벤트가 동작하지 않는다.

 

const $list = document.querySelector("ul");
const $listItem = document.querySelectorAll("li");
$list.addEventListener("click", (e) => {
  const { target } = e;
  if (target.tagName == "LI") {
    console.log(target.innerText);
  }
  // or
  if (target.matches("li")) {
    console.log(target.innerText);
  }
});

const $button = document.querySelector("button");
$button.addEventListener("click", () => {
  const $newItem = document.createElement("li");
  $newItem.innerText = "new Item";
  $list.appendChild($newItem);
});

 

최상단 <ul /> 태그에 이벤트를 위임해 두고, 이벤트가 발생한 타깃을 비교해 이벤트 동작을 제어할 수 있다. <li /> 태그에서 동작할 이벤트를 <ul /> 태그에서 제어하는 것이다.

 

동적으로 요소를 추가/삭제하고자 하는 경우 이벤트 위임을 사용한다.

 

이벤트 위임 장점

 

  • 각 하위 항목에 이벤트를 등록하지 않아, 상위 요소에서만 이벤트 핸들러가 필요해 메모리 사용 효율성을 높일 수 있다.
  • 요소의 추가/제거 시 해당 요소에 할당된 핸들러를 추가/제거할 필요가 없다.

 

이벤트 위임 단점

 

  • 이벤트 위임을 위해서 버블링이 선행되어야 하는데 focus 이벤트와 같은 특정 이벤트는 버블링이 되지 않는다.
  • 상위 요소에 할당된 핸들러는 이벤트의 필요성에 상관없이 모든 하위 이벤트에 응답해야 해서 과부하가 발생할 수 있다(그러나 크게 신경 쓸 정도는 아니고 무시해도 된다).

 

참고

 

 

Event | PoiemaWeb

이벤트(event)는 어떤 사건을 의미한다. 브라우저에서의 이벤트란 예를 들어 사용자가 버튼을 클릭했을 때, 웹페이지가 로드되었을 때와 같은 것인데 이것은 DOM 요소와 관련이 있다. 이벤트가 발

poiemaweb.com

 

Event - Web API | MDN

Event 인터페이스는 DOM 내에 위치한 이벤트를 나타냅니다.

developer.mozilla.org

 

[JS] Event Delegation(이벤트 위임)

이벤트 위임을 이해하기 위해서는 이벤트 버블링과 캡쳐링에 대한 이해가 필요합니다. 이벤트 버블링과 캡쳐링에 대해 잘 모르시는 분은 아래 글을 참고하시면 될 것 같습니다. [JS] Event Bubbling

hangeoreum.tistory.com

 

 

[JS] Event Bubbling과 Capturing

웹페이지에서 가장 중요한 것 중 하나를 뽑자면 사용자와의 인터렉션을 꼽을 수 있습니다. 인터렉션은 브라우저 이벤트로 구현할 수 있고, 주로 DOM 이벤트를 이용합니다. 이벤트에는 마우스,

hangeoreum.tistory.com

 

 

이벤트 버블링, 이벤트 캡처 그리고 이벤트 위임까지

(기본) 이벤트 버블링, 이벤트 캡처링, 그리고 이벤트 위임까지 이벤트 전달 방식과 관련된 모든 것을 파헤쳐 봅니다.

joshua1988.github.io

 

 

버블링과 캡처링

 

ko.javascript.info

반응형

'Developer > TI' 카테고리의 다른 글

Babel, Webpack(바벨, 웹팩)  (0) 2022.11.22
프로미스, 콜백 함수  (0) 2022.11.08
자바스크립트 script async/defer 어트리뷰트  (0) 2022.10.21
브라우저 렌더링 과정 정리  (2) 2022.10.11
REST API, RESTful API 정리  (0) 2022.09.29