▼ HTML, CSS 문서
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="content">
content
<h1 id="title">오늘 할 일</h1>
<ol id="list">
list
<li class="item">자바스크립트 공부</li>
<li class="item">구름이 산책</li>
<li class="item">게임</li>
<li class="item">독서</li>
<li class="item">청소</li>
</ol>
</div>
<script src="index.js"></script>
</body>
</html>
body * {
padding: 7px 10px;
margin: 5px 8px;
color: #ffffff;
font-size: 12px;
background-color: rgba(0, 0, 0, 0.4);
}
h1 {
font-size: 20px;
font-weight: 500;
}
아래는 각 요소별로 클릭 타입에 이벤트 핸들러들을 등록한 상태이다.
// 이벤트 버블링 (Event Bubbling)
const content = document.querySelector("#content");
const title = document.querySelector("#title");
const list = document.querySelector("#list");
const items = document.querySelectorAll(".item");
content.addEventListener("click", function () {
console.log("content Event");
});
title.addEventListener("click", function () {
console.log("title Event");
});
list.addEventListener("click", function () {
console.log("list Event");
});
for (let item of items) {
item.addEventListener("click", function () {
console.log("item Event");
});
}
▽ 실행해보면 다음과 같다
▽ 결과를 확인하기위해 콘솔창에서 콘텐츠 영역을 클릭하면 콘텐트 이벤트가 정상적으로 출력된다.
▽ 하지만 타이틀을 클릭하게 되면 콘텐트 이벤트 핸들러도 동작하고 리스트도 똑같이 출력이되는 현상이 발행한다.
▽ 또한 리스트 내의 아이템을 클릭하면 아이템,리스트,콘텐트 이벤트가 출력된다.
이러한 현상을 '이벤트 버블링'이라고 부른다.
이벤트 버블링
버블링의 원리는 생각보다 간단하다.
어떤 하나의 요소에 이벤트가 발생하게 되면
그 요소에 할당된 이벤트 핸들러가 동작하고
거기서 끝이 아니라 이어서 같은 타입의 이벤트에 한해서 부모 요소의 핸들러도 동작하게 되는 것이다.
그렇게 가장 최상단의 윈도우 객체를 만날 때까지 이 과정이 반복되면서
요소 각각의 할당된 모든 이벤트 핸들러가 동작하는 원리이다.
이벤트가 발생하면 가장 아래 자식 요소부터
부모 요소를 거슬러 올라가면 발생하는 모양이
마치 물속에서 올라오는 거품과 닮았다고 해서 '버블링' 이라는 이름이 붙었다고 한다.
이러한 이유 때문에 아이템 영역을 클릭했지만
버블링에 의해서 아이템의 부모 요소인 리스트,
리스트의 부모 요소인 콘텐트의 이벤트 핸들러도 함께 동작했던 것이다.
하지만 이벤트 객체의 'target' 프로퍼티를 같이 콘솔에 출력해보면
버블링이 되면서 각 이벤트 핸들러가 동작할 때
해당 핸들러가 등록된 요소가 'target'이 될거라고 생각할 수 있지만 전혀 그렇지 않다.
// 이벤트 버블링 (Event Bubbling)
const content = document.querySelector("#content");
const title = document.querySelector("#title");
const list = document.querySelector("#list");
const items = document.querySelectorAll(".item");
content.addEventListener("click", function (e) {
console.log("content Event");
console.log(e.target);
});
title.addEventListener("click", function (e) {
console.log("title Event");
console.log(e.target);
});
list.addEventListener("click", function (e) {
console.log("list Event");
console.log(e.target);
});
for (let item of items) {
item.addEventListener("click", function (e) {
console.log("item Event");
console.log(e.target);
});
}
이와 같이 target은 변하지 않는다는걸 볼 수 있는데
사실 이 덕분에 부모 요소의 핸들러들이
최초의 이벤트가 발생한 위치를 정확하게 파악할 수 있게 되는 것이다.
즉 이벤트 버블링이 일어나도 이벤트 객체의 target 프로퍼티는 변하지 않고
처음 이벤트가 발생한 시작점을 담고 있다
currentTarget 프로퍼티
그런데 만약 실행 중인 핸들러가 할당된 요소,
즉 이벤트 핸들러가 등록된 요소에 접근하고자 할 때 'currentTarget' 프로퍼티를 사용한다.
// 이벤트 버블링 (Event Bubbling)
const content = document.querySelector("#content");
const title = document.querySelector("#title");
const list = document.querySelector("#list");
const items = document.querySelectorAll(".item");
content.addEventListener("click", function (e) {
console.log("content Event");
console.log(e.currentTarget);
});
title.addEventListener("click", function (e) {
console.log("title Event");
console.log(e.currentTarget);
});
list.addEventListener("click", function (e) {
console.log("list Event");
console.log(e.currentTarget);
});
for (let item of items) {
item.addEventListener("click", function (e) {
console.log("item Event");
console.log(e.tcurrentTarget);
});
}
실행해보면 실제로 이벤트 핸들러가 동작하는 요소가 출력되는 것을 확인할 수 있다.
자주 사용되는건 아니지만 이벤트 객체에 이런 프로퍼티가 있다는 정도로 알면된다.
이벤트 버블링 멈추는 방법
이벤트 버블링을 멈추려면
이벤트 객체에 'stopPropagation' 메소드를 사용하면 간단하게 버블링을 멈출 수 있다.
한번 item에 이 메소드를 적용해봤을 때 클릭해보면 버블링이 일어나지 않는걸 확인할 수 있다.
// 이벤트 버블링 (Event Bubbling)
const content = document.querySelector("#content");
const title = document.querySelector("#title");
const list = document.querySelector("#list");
const items = document.querySelectorAll(".item");
content.addEventListener("click", function (e) {
console.log("content Event");
console.log(e.currentTarget);
});
title.addEventListener("click", function (e) {
console.log("title Event");
console.log(e.currentTarget);
});
list.addEventListener("click", function (e) {
console.log("list Event");
console.log(e.currentTarget);
});
for (let item of items) {
item.addEventListener("click", function (e) {
console.log("item Event");
console.log(e.currentTarget);
e.stopPropagation(); // 이벤트 버블링 해결
});
}
메소드 하나로 이렇게 간단하게 버블링을 막을 수 있지만
필요한 경우가아니라면 가급적 버블링을 막는 걸 피하는 것이 좋다.
지금처럼 아이템 부분에서 이벤트 버블링을 막아버리게 되면
바로 위에 있는 리스트뿐만 아니라 모든 부모 요소의 입장에서
아이템 영역만큼의 이벤트를 발생시킬 범위가 사라져버리게 되고
그 부분만 원하는 이벤트 결과를 얻지 못하기 때문에 버블링 막는걸 권장하지 않는다.
(물론 설계 잘한다면 큰 문제는 없다만..)
하지만 이벤트를 좀 더 제대로 다루기 위해서는
반드시 이해하고 있어야하는 부분이기에 알고있는 것이 좋다.
▶GitHub
'JS' 카테고리의 다른 글
(17) Interactive JavaScript (Event) - 브라우저의 기본 동작 제어 (0) | 2024.11.11 |
---|---|
(16) Interactive JavaScript (Event) - 이벤트 위임 (0) | 2024.11.08 |
(14) Interactive JavaScript (Event) - 이벤트 객체 (0) | 2024.11.06 |
(13) Interactive JavaScript (Event) - 이벤트 핸들러 등록하기 (0) | 2024.11.06 |
(12) Interactive JavaScript - 비표준 속성 (0) | 2024.11.05 |