보글보글보글-이벤트 버블링

MoonEn·2023년 1월 22일
0

JavaScript

목록 보기
1/5
post-thumbnail

JavaScript로 HTML을 가지고 놀면서 공부하던 중에 아주 특이한 동작을 하는 것이 있어서 정리해보려고 한다. 그것은 이름하야 이벤트 버블링이다. 간단한 예시를 들어보자.

HTML

        <div id="content">
            content
            <div id="title">오늘할 일</div>
            <ul id="list">
                List
                <li class="item">HTML 공부</li>
                <li class="item">자바스크립트 공부</li>
                <li class="item">강의 시청</li>
                <li class="item">코딩 테스트 연습</li>
                <li class="item">독서</li>
            </ul>
        </div>

JavaScript

const content = document.getElementById("content");
const title = document.getElementById("title");
const list = document.getElementById("list");
const items = document.getElementsByClassName("item");
content.addEventListener("click", () => {
    console.log("content를 눌렀음.");
});
title.addEventListener("click", () => {
    console.log("title를 눌렀음.");
});
list.addEventListener("click", () => {
    console.log("list를 눌렀음.");
});
for (let item of items) {
    item.addEventListener("click", () => {
        console.log("item을 눌렀음");
    });
}

그럼 저기서 item을 클릭하면 어떻게 될까? console 창에 "item을 눌렀음"이라고 떠야할 것 같다. 하지만 실제로는 아래처럼 부모 요소들에 있는 것까지 모두 출력된다.

이것이 바로 이벤트 버블링이다.

이벤트 버블링

이벤트 버블링은 자식 요소에서 이벤트 핸들러가 실행될 때, 부모 요소의 이벤트 핸들러까지 실행되는 것을 버블링이라고 한다. 그래서 li요소만 눌러도 그 부모 요소인 ul요소와 div요소의 이벤트 핸들러가 실행된 것이다.
생각을 해보면 특정 요소에게만 특정 이벤트를 실행하고 싶은데, 그 부모 요소의 이벤트 핸들러가 실행되다니 고려할 사항이 너무 많을 것 같다. 그래서 이벤트 버블링을 막을 수 있는 방법도 있다.

이벤트 핸들러 동작 막기

간단하게 stopPropagation()메서드를 사용하면 된다. 이벤트 객체에 사용하는 것으로 부모 요소가 접근하지 못하게 막는 것이다.

list.addEventListener("click", (event) => {
    console.log("list를 눌렀음.");
    event.stopPropagation()
});

이전과 달리 이벤트가 1개만 실행된 것을 알 수 있다.

자! 그렇다면 모든 요소의 이벤트 핸들러에 stopPropagation()을 넣으면 되는 것일까? 정답은 노! 이다. 부모 요소의 접근을 막는 것은 득보다 실이 많기 때문이다. 오히려 이벤트 버블링이 발생해서 얻는 이득이 많다. 어떤 이점이 있는지 알아보도록 하자.

이벤트 버블링 활용

이벤트 위임

이벤트가 부모 요소로 전달되는 버블링을 활용하여 부모 요소의 이벤트를 자식 요소에게 위임을 할 수 있다. 예를 들어 자식 요소에서 click이벤트 발생 시, 부모 요소의 click이벤트도 실행된다. 이 때, 자식 요소에는 이벤트 핸들러를 등록하지 않고, 부모 요소에만 등록한다면 부모 요소의 이벤트가 실행된다. 아래의 예를 살펴보자.

const content = document.getElementById("content");
content.addEventListener("click", () => {
    console.log("content를 눌렀음.");
});

기존의 코드에서 부모 요소에 있는 이벤트 핸들러를 제외한 나머지 이벤트 핸들러를 지웠다. 그리고 동일하게 item요소를 눌러보자.
이 때 버블링으로 부모 요소인 Content의 이벤트 핸들러가 동작하게 된다.

간단하게 정리하면 아래와 같다.

target

자, 이제 이벤트 위임에 대해서 간략하게 알게 되었다. 이제 나는 이벤트 위임을 통해서 item을 클릭했을 때, item의 이름을 출력하고 class에 done을 추가하는 코드를 이벤트를 만들었다.

const content = document.getElementById("content");
content.addEventListener("click", (event) => {
    console.log(event.target.innerText)
    event.target.classList.toggle('done')
});

이 코드의 문제점은 무엇일까? 바로 item뿐만 아니라 그 위의 부모 요소들을 클릭했을 때도 동작을 한다는 것이다. 이것을 해결할 수 있는 방법은 무엇일까? 바로 이벤트 객체의 target속성을 이용하는 방법이다. 다시 처음의 코드로 돌아가서 console.log(event.target)을 추가해서 이벤트 버블링을 실행해보자.

content.addEventListener("click", (event) => {
    console.log("content를 눌렀음.");
    console.log(event.target);
});
title.addEventListener("click", (event) => {
    console.log("title를 눌렀음.");
    console.log(event.target);
});
list.addEventListener("click", (event) => {
    console.log("list를 눌렀음.");
    console.log(event.target);
});
for (let item of items) {
    item.addEventListener("click", (event) => {
        console.log("item을 눌렀음");
        console.log(event.target);
    });
}

이벤트 버블링이 일어날 때, 이벤트 객체의 Target속성은 처음 이벤트 버블링이 시작된 이벤트 객체가 담겨있다. 이 target 속성을 이용하면 이벤트 위임 시, 원하는 요소에서 의도한 동작이 일어날 수 있도록 처리할 수 있다.(target을 사용하지 않는 다양한 방법들도 있음!)

이제 item에만 이름 출력과 class를 추가하도록 수정해보자.

content.addEventListener("click", (event) => {
    if(!event.target.classList.contains("item")) return
    console.log(event.target.innerText)
    event.target.classList.toggle('done')
});

위 코드는 target의 classList에 "item"을 포함하고 있지 않으면, 바로 return 하여 함수를 끝나게 만든 것이다. 그리고 "item"을 포함하고 있다면, 그 아래의 코드가 실행된다. 이렇게 이벤트 위임을 통해서 부모 요소의 이벤트를 자식 요소에서 사용할 수 있게 하고, target으로 특정 요소에서만 이벤트를 사용할 수 있게 만들었다.

그렇다면 왜 이런 방식으로 버블링을 사용할까?

왜?

listitem들이 추가 된다고 생각해보자. 각 item들에 이벤트 핸들러를 추가한다면, item이 추가될 때 마다 이벤트 핸들러도 추가를 해야할 것이다. 하지만 반대로 이벤트 위임을 하게 된다면, 부모 요소에만 이벤트 핸들러를 추가하고 item만 구분하여 이벤트 핸들러를 동작하게 만들 수 있다. 그럼 item이 몇 개가 추가되든 이벤트 핸들러 추가 없이 모두 동일하게 동작할 수 있게 만들 수 있다. 그렇기 때문에 이벤트 버블링을 유용하게 사용할 수 있는 것이다.

마무리

이벤트 버블링에 관해 간단하게 살펴 보았다. 처음에 이벤트 버블링이라는 것이 있다는 점에 의아하였다. 이벤트가 발생하면 부모 요소도 같은 이벤트가 동작한다? 코딩을 할 때 매우 복잡해 질 것이라 생각했다. 물론 복잡하다. 하지만 사용하는 방법을 알고 나니 조금은 왜 이렇게 만든 것인지 이해가 된다. 오히려 익숙해지면 이 방법이 더 편할 것 같다. 아직은 익숙하지 않으니 이것저것 고려할 것이 많지만 향후에 좀 더 익숙해 진다면 날아다니지 않을까 싶다.(이것은 나의 바람) 그러기 위해서 나는 또 공부하러 간다.

profile
개발자를 꿈꾸는 직장인

0개의 댓글