08. 이벤트 위임

📌 8-1 이벤트 전파란?

이벤트가 생성된 시점에서 이벤트의 전파 과정으로 3가지 과정인 캡처링, 타깃, 버블링 과정으로 구분된다.

  • 캡처링: 상위 요소에서 발생한 이벤트가 하위 요소 방향으로 전파되는 과정
  • 타깃: 실제로 이벤트가 발생한 대상
  • 버블링: 하위 요소에서 발생한 이벤트가 상위 요소 방향으로 전파되는 과정

📌 8-2 이벤트 전파 과정

각각의 자식과 부모 태그 요소에 이벤으를 등록해서 전파 과정을 알아본다.

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>Event</title>
</head>
<body>
    <ul id="ul">
        <li id="li1">1</li>
        <li id="li2">2</li>
    </ul>
    <script>
        const $ul = document.querySelector("#ul");
        const $li1 = document.querySelector("#li1");
        const $li2 = document.querySelector("#li2");
        // 부모와 자식노드에 각각 내용이 다른 이벤트를 설정한다.
        $ul.addEventListener("click", () => {
            console.log("ul 클릭 이벤트 입니다.")
        });
        // 1을 클릭하는 순간 버블링으로 인해 ul 이벤트도 같이 발생한다.
        $li1.addEventListener("click", () => {
            console.log("li 클릭 이벤트 입니다.");
        });
    </script>
</body>
</html>

이벤트 버블링으로 인하여 콘솔창에 아래와 같은 내용이 나타나게 된다.

li 클릭 이벤트 입니다.
ul 클릭 이벤트 입니다.

이벤트 캡처링도 일어났지만 콘솔창에 확인이 되지않는 이유는 캡처링은 의도적으로 켜줘야 실행이 가능하다.

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>JS</title>
</head>
<body>
    <ul id="ul">
        <li id="li1">1</li>
        <li id="li2">2</li>
    </ul>
    <script>
        const $ul = document.querySelector("#ul");
        const $li1 = document.querySelector("#li1");
        const $li2 = document.querySelector("#li2");
        // 부모인 ul 태그의 캡처링 이벤트 설정
        $ul.addEventListener("click", (e) => {
            console.log(`이벤트 단계 : ${e.eventPhase}`);
            console.log(`이벤트 타깃 : ${e.target}`);
        }, { capture: true });
        // 자식인 li 태그의 이벤트 설정
        $li1.addEventListener("click", (e) => {
            console.log(`이벤트 단계 : ${e.eventPhase}`);
            console.log(`이벤트 타깃 : ${e.target}`);
        });
        $li2.addEventListener("click", (e) => {
            console.log(`이벤트 단계 : ${e.eventPhase}`);
            console.log(`이벤트 타깃 : ${e.target}`);
        });
        // 부모인 ul 태그의 이벤트 설정
        $ul.addEventListener("click", (e) => {
            console.log(`이벤트 단계 : ${e.eventPhase}`);
            console.log(`이벤트 타깃 : ${e.target}`);
        });
    </script>
</body>
</html>

1이나 2를 클릭하게 되면 콘솔창 결과는 아래와 같이 나오게 된다.

// 1 : 이벤트 캡처링
이벤트 단계 : 1
이벤트 타깃 : [object HTMLLIElement]
// 2 : 이벤트 타깃
이벤트 단계 : 2
이벤트 타깃 : [object HTMLLIElement]
// 3 : 이벤트 버블링
이벤트 단계 : 3
이벤트 타깃 : [object HTMLLIElement]

📌 8-3 이벤트 위임

이벤트를 관리하는 방법으로 각각 노드에 이벤트를 등록하는 것이 아니라 상위 요소에 이벤트를 등록해서 관리하는 방법이다.

  • 각각의 요소의 이벤트를 모두 등록하지 않아도 됨으로 메모리 절약이 가능하다.
  • 코드의 재사용성이 높아진다.
  • 이벤트 버블링의 특성을 이용한다.

🔥 이벤트 위임을 하지 않을 경우

<!DOCTYPE html>
<html lang="ko">

<head>
    <meta charset="UTF-8">
    <title>Event</title>
</head>
<body>
    <!--li의 요소를 클릭했을 경우 해당 li의 이름이 h3의 텍스트로 나타난다.-->
    <ul class="animals" style="width: 400px; background-color: gold;">
        <li class="animal-li" data-animal-index="Dog">Dog</li>
        <li class="animal-li" data-animal-index="Cat">Cat</li>
        <li class="animal-li" data-animal-index="Pig">Pig</li>
        <li class="animal-li" data-animal-index="Cow">Cow</li>
    </ul>

    <h3 class="title"></h3>

    <script>
        // 모든 li 태그 요소를 가져온다.
        const $animalsLi = document.querySelectorAll(".animal-li");
        // 텍스트를 표시할 공간
        const $title = document.querySelector(".title");
        // live 객체로 오기 때문에 안전하게 배열로 변환한다.
        const list = [...$animalsLi];
        // 이벤트 핸들러 함수를 생성
        const eventHandler = (event) => {
            const name = event.target.dataset.animalIndex;
            // title의 텍스트를 설정
            $title.innerText = name;
        }
        // 각각의 노드의 이벤트를 설정
        list.forEach((node) => {
            node.addEventListener("click", eventHandler);
        });
    </script>
</body>
</html>

이벤트 위임을 하지 않을 경우 문제점은 이벤트를 많이 등록하게 됨으로 메모리에 부담이 좋지 않다.

🔥 이벤트 위임을 한 경우

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>Event</title>
</head>
<body>
    <ul class="animals" style="width: 400px; background-color: gold;">
        <li class="animal-li" data-animal-index="Dog">Dog</li>
        <li class="animal-li" data-animal-index="Cat">Cat</li>
        <li class="animal-li" data-animal-index="Pig">Pig</li>
        <li class="animal-li" data-animal-index="Cow">Cow</li>
    </ul>

    <h3 class="title"></h3>

    <script>
        // li의 태그들의 상위 태그 요소인 ul 태그를 가져온다.
        const $animals = document.querySelector(".animals");
        // 텍스트를 표시할 공간
        const $title = document.querySelector(".title");
        // 이벤트 위임
        $animals.addEventListener("click", (event) => {
            // 해당 이벤트를 발생한 target의 dataset만 추출
            const name = event.target.dataset.animalIndex;
            // 이벤트를 발생시킨 구간이 animal-li가 아닐경우 이벤트 종료 (예외처리)
            if(event.target.className !== "animal-li") return;
            // title의 텍스트를 설정
            $title.innerText = name;
        });
    </script>
</body>
</html>

이벤트 위임을 할 경우 메모리절약과 동시에 코드의 가독성과 코드의 길이가 줄어들어 유지보수에도 도움이 된다.

📌 8-4 이벤트 객체 (target, currentTarget)의 차이점

  • target: 현재 이벤트를 발생시키는 요소
  • currentTarget: 현재 이벤트 핸들러가 바인딩 되어 있는 요소
<!DOCTYPE html>
<html lang="ko">

<head>
    <meta charset="UTF-8">
    <title>Event</title>
</head>
<body>
    <ul class="animals" style="width: 400px; background-color: gold;">
        <li class="animal-li" data-animal-index="Dog">Dog</li>
        <li class="animal-li" data-animal-index="Cat">Cat</li>
        <li class="animal-li" data-animal-index="Pig">Pig</li>
        <li class="animal-li" data-animal-index="Cow">Cow</li>
    </ul>

    <h3 class="title"></h3>

    <script>
        const $animals = document.querySelector(".animals");
        const $title = document.querySelector(".title");
        // 현재 이벤트 핸들러가 바인딩 된 요소는 $animals이므로 currentTarget은 ul 태그를 가리킨다.
        $animals.addEventListener("click", (event) => {
            // 결과 : li
            console.log(event.target);
            // 결과 : ul
            console.log(event.currentTarget);
        });
    </script>
</body>
</html>

좋은 웹페이지 즐겨찾기