FE/JavaScript

[JS] 이벤트 위임

mandelina 2022. 5. 22. 17:13

코딩을 하던중 아이템이 클릭 이벤트에 반응하도록 등록을 하고 싶어 for문을 이용하여 addEventListener을 등록해주었다. 하지만 이렇게 하나하나 등록하는것은 부적합한 코드인것 같아 알아보니 '이벤트 위임'을 알게되었다. (사실 이미 배웠던거지만 생각이 안났던 ㅜㅜ..)

 

 

실제로 DOM요소에 이벤트 핸들러를 많이 등록할 수록 성능저하의 원인이 된다.

그렇기에 DOM요소에 여러개의 이벤트 핸들러를 등록할 필요 없이 이벤트 위임을 통해 접근하는 방법을 알아보자.

 

 


 

 

이벤트 위임

- 이벤트 위임을 사용하면 각 요소마다 핸들러를 할당하지 않고 요소의 공통 조상에 이벤트 핸들러를 하나만 할당해도 여러 요소를 한꺼번에 다룰 수 있다.

 

- event.target을 이용해 실제 어디서 이벤트가 발생했는지 알 수 있다.

 

 

[안좋은 예]

    <script>
      const parent = document.querySelector(".parent");
      const $li = document.querySelectorAll("li");

      for (let i = 0; i < $li.length; i++) {
        $li[i].addEventListener("click", function (event) {
          console.log(event.target);
        });
      }
    </script>

 

내가 처음에 시도했던 방법이다.

for문을 이용하여 addEventListener을 주었지만 이 경우 성능저하를 일으킬 수 있다.

 

 

 

[이벤트 위임 예제]

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <link rel="stylesheet" href="../reset.css" />
    <style></style>
  </head>

  <body>
    <article class="parent">
      <ol>
        <li><button class="btn-first" type="button">버튼1</button></li>
        <li><button type="button">버튼2</button></li>
        <li><button type="button">버튼3</button></li>
      </ol>
    </article>

    <script>
      const parent = document.querySelector(".parent");
      parent.addEventListener("click", function (event) {
        console.log(event.target);
        if (event.target.nodeName === "BUTTON") {
          event.target.innerText = "버튼4";
        }
      });
    </script>
  </body>
</html>

 

이렇게 이벤트 리스너를 parent에만 등록하고, event.target을 이용하여 parent 하위 자식 요소들을 컨트롤 할 수 있다.

 

 

 

이벤트 위임에서 주의해야할 점

이벤트 위임을 통해 하위 DOM 요소에서 발생한 이벤트를 처리할때, 이벤트를 실제로 발생시킨 DOM요소개발자가 기대한 DOM요소가 아닐수도 있다는 것이다.

 

예를들어 ul에 이벤트위임을 등록하고 li를 컨트롤 하고싶었지만 li가 아닌 나머지 공간, 또는 li안의 다른 태그들에도 반응 할 수 있다는 점이다.

 

 

 

1. 나머지공간(이벤트가 발생되지 않아도 되는 공간)에서 이벤트가 발생하지 않도록 해결

 

function activate ({ target }) {
	if (!target.matches('.parent > ol > li ')) return; 
    //이벤트를 발생시킨 요소가 parent의 ol>li 이가 아니라면 무시한다.
    .
    .
    .
 }
 
 parent.onClick = activate; // parent에 이벤트 바인딩

 

그러면 parent의 하위요소인 li요소만 이벤트에 반응하게 만들 수 있다.

 

 


 

2. 내가 원하는 요소의 하위요소까지 반응하는 부분 해결

 

 간단히 css속성을 아래와 같이 주면된다.

  pointer-events: none;

하지만 이 속성은 하위요소의 이벤트를 이용해야 할때는 부적합 할 수 있다.