이벤트 버블링은 특정 화면 요소에서 이벤트가 발생했을 때 해당 이벤트가 더 상위의 화면 요소들로 전달되어 가는 특성을 의미한다.
<div onclick="alert('div에 할당한 핸들러!')">
<em><code>EM</code>을 클릭했는데도 <code>DIV</code>에 할당한 핸들러가 동작합니다.</em>
</div>
위 코드의 핸들러는 div
태그에 할당되어 있지만, em
이나 code
같은 중첩 태그를 클릭해도 동작하게 된다. (= 버블링)
버블링의 원리는 간단하다. 한 요소에 의해 이벤트가 발생하면 이 요소에 할당된 핸들러가 동작하고 이어서 부모 요소의 핸들러가 동작한다. 가장 최상단의 조상 요소를 만날 때까지 이 과정이 반복되면서 요소 각각에 할당된 핸들러가 동작한다
1) p
에 할당된 onclick
핸들러가 동작한다.
2) div
에 할당된 핸들러가 동작한다
3) form
에 할당된 핸들러가 동작한다
4) document
객체를 만날 때까지, 각 요소에 할당된 onclick
핸들러가 동작한다.
따라서 p
태그를 클릭하면 alert
창이 3번 동작하게 되는 것이다.
부모 요소의 핸들러는 이벤트가 정확히 어디에서 발생했는지에 대한 자세한 정보를 얻을 수 있다.
event.target과 this는 차이점이 있다.
이벤트 버블링은 타깃 이벤트에서 시작해서 html 요소를 거쳐 document 객체를 만날 때까지 각 노드에서 모두 발생한다. 따라서 핸들러에게 이벤트를 처리하고 난 후 버블링을 중단하도록 명령할 수 있는데, 이벤트 객체의 메서드인 `event.stopPropagation()을 사용하면 된다.
<body onclick="alert(`버블링은 여기까지 도달하지 못합니다.`)">
<button onclick="event.stopPropagation()">클릭해 주세요.</button>
</body>
--> button을 클릭해도 body.onclick은 동작하지 않는다.
Js의 대부분 이벤트는 이벤트 흐름의 이벤트 캡처링, 버블링 단계를 거친다 일반적으로 addEventListener() 메서드의 세 번째 매개변수로 true를 전달하지 않은 이상 이벤트 캡처링 단계는 수행되지 않고 이벤트 버블링 단계를 거치는데, 이벤트 버블링은 타겟에서 이벤트가 발생하면 타겟에서 위로 이벤트를 전파하는 것을 말한다.
<div id="div-content">
<span id="span-content">
<button id="btn">버튼</button>
</span>
</div>
const divNode = document.getElementById("div-content");
const spanNode = document.getElementById("span-content");
const btnNode = document.getElementById("btn");
divNode.addEventListener("click", function () {
console.log("divNode Click");
});
spanNode.addEventListener("click", function () {
console.log("spanNode Click");
});
btnNode.addEventListener("click", function () {
console.log("btnNode Click");
});
button
태그에서 click
이벤트가 발생하면, 해당 이벤트는 최상위 객체인 Document
까지 전파된다. 이벤트 위임은 개별 요소에 이벤트 리스너(혹은 이벤트 핸들러)를 생성하는 대신 상위 요소에 이벤트 리스너를 생성하고 해당 이벤트 리스너에서 어떤 요소에서 이벤트가 발생했는지에 대한 조건문을 설정하도록 하는 것이다.
<div id="div-content">
<span id="span-content">
<button id="btn">버튼</button>
</span>
</div>
// 방법 1)
// 상위 요소인 id가 "div-content"인 div 노드만 접근함
const divNode = document.getElementById("div-content");
// div 노드에만 이벤트 리스너를 설정
divNode.addEventListener("click", function (e) {
const id = e.target.id;
// 이벤트 객체의 target.id로 조건문을 설정
if (id === "div-content") {
console.log("div-content id Click");
} else if (id === "span-content") {
console.log("span-content id Click");
} else if (id === "btn") {
console.log("btn id Click");
}
});
----------------------------------------------------------------
// 방법 2)
const divNode = document.getElementById("div-content");
divNode.addEventListener("click", function (e) {
const tagName = e.target.tagName;
if (tagName === "DIV") {
console.log("div Click");
} else if (tagName === "SPAN") {
console.log("span id Click");
} else if (tagName === "BUTTON") {
console.log("button Click");
}
});
div가 상위요소 이므로 div에만 이벤트 핸들러를 등록한다. 그리고 어디서 발생했는지 알아야 하므로 이벤트 객체(e)를 사용해 어떤 요소(e.target)에서 이벤트가 발생했는지에 대한 조건문을 설정한다.
여러 개의 이벤트 리스너를 등록하지 않고 상위 요소에 하나의 이벤트 리스너만 등록하는 것을 이벤트 위임이라고 한다.
메모리 사용량 감소 : JavaScript에서 함수는 객체이다. 함수가 많을수록 메모리 사용량이 증가하고 함수가 적을수록 메모리 사용량이 감소한다.
개발자가 의도한 대로 동작 : 여러 개 이벤트 리스너를 등록하면 이벤트 버블링에 의해 상위 요소의 이벤트 리스너도 실행ㅇ되는데 이를 방지할 수 있다.