캡처링: window로부터 이벤트가 발생한 요소까지 이벤트를 전파한다
버블링: 이벤트가 발생한 요소부터 window까지 이벤트를 전파한다
특정 요소에서 이벤트가 발생했을 때 해당 이벤트가 상위 요소들로 전달되어 가는 특성을 의미한다
<div class="one">
one
<div class="two">
two
<div class="three">
three
</div>
</div>
</div>
const div = document.querySelectorAll('div')
div.forEach((el) => {
el.addEventListener('click', bubbling)
})
function bubbling(e) {
console.log(e.currentTarget.className);
}
최하위 태그인 three를 클릭하면, 3개의 이벤트가 모두 발생된다. 그 이유는 브라우저가 이벤트를 감지하는 방식 때문이다
✅ 특정 요소에서 이벤트가 발생했을 때 그 이벤트를 최상위에 있는 요소까지 전파시킨다
주의할 점은, 각 태그마다 이벤트가 등록되어 있기 때문에 상위 요소로 이벤트가 전달되는 것을 확인할 수 있는 것이다. 만약 이벤트가 특정 태그에만 달려 있다면 동작 결과는 확인할 수 없다
이와 같은 하위에서 상위 요소로의 이벤트 전파 방식을 이벤트 버블링이라고 한다
거의 모든 이벤트는 버블링 된다
⇒ focus 와 같이 버블링되지 않는 이벤트도 있지만, 대부분 버블링된다
✅ event.target
vs event.currentTarget(=this)
event.target
은 실제 이벤트가 시작되는 요소다. 버블링이 진행되어도 변하지 않는다
event.currentTarget
은 현재 요소로, 실행 중인 핸들러가 할당된 요소를 참조한다
이벤트 버블링과 반대 방향으로 진행되는 이벤트 전파 방식이다
버블링이 전달의 느낌이라면 캡처는 탐색의 느낌이다
*이벤트 버블링을 반대 방향으로 탐색하고 싶을 때
: 이벤트 리스너를 등록할 때 capture: true
를 설정해주면 된다
const div = document.querySelectorAll('div')
div.forEach((el) => {
el.addEventListener('click', bubbling, {
capture: true // default - false // capture라고 명시해주지 않고 true만 써도 무방
})
})
function bubbling(e) {
console.log(e.currentTarget.className);
}
이벤트가 전파되는 것을 막는 메서드
const div = document.querySelectorAll('div')
div.forEach((el) => {
el.addEventListener('click', bubbling)
})
function bubbling(e) {
e.stopPropagation() 👈
console.log(e.currentTarget.className);
}
이벤트 버블링: 클릭한 요소의 이벤트만 발생시키고 상위 요소로 이벤트를 전달하는 것을 방해한다
이벤트 캡처링: 클릭한 요소의 최상위 요소의 이벤트만 동작시키고 하위 요소들로 이벤트를 전달하지 않는다
*버블링을 막으면 추후 문제가 될 수 있기 때문에, 꼭 필요한 경우를 제외하고는 버블링을 막지 말자
하나의 이벤트를 처리하는 핸들러가 여러개인 상황에서, event.stopPropagation()은 위쪽으로 일어나는 버블링은 막아주지만, 다른 핸들러들이 동작하는 건 막지 못한다
버블링을 멈추고, 요소에 할당된 다른 핸들러의 동작도 막으려면 event.stopImmediatePropagation()을 사용해야 한다
하위 요소에 각각 이벤트를 붙이지 않고 상위 요소에서 하위 요소의 이벤트들을 제어하는 방식이다
<ul id="parent-list">
<li id="item1">Item 1</li>
<li id="item2">Item 2</li>
<li id="item3">Item 3</li>
</ul>
document.getElementById("todo-list").addEventListener("click", function (e) {
if (e.target && e.target.nodeName == "LI") {
console.log(`List item ${e.target.id} was clicked!`);
}
});
<ul id="todo-list">
<li class="todo-item">
<span class="item-name">${listInputValue}</span>
<div class="item-btn">
<button type="button" class="done-btn">완료</button>
<button type="button" class="edit-btn">수정</button>
<button type="button" class="remove-btn">삭제</button>
</div>
</li>
</ul>
const toDoList = document.querySelector('#todo-list')
toDoList.addEventListener("click", (e) => {
if(e.target.classList.contains("edit-btn")) {
editToDoList(e)
}
if(e.target.classList.contains("remove-btn")) {
removeToDoList(e)
}
})
장점