.png)
Event Delegation의 속성을 활용하여 Vanila JS를 구현해보는 과정에서 이벤트 버블링과 캡쳐에 대한 지식이 정리가 되어있지 않음을 느껴 이 글을 작성하게 되었다.
클릭이나 스크롤을 내리는 등 사용자와 웹페이지가 상호작용하며 브라우저가 감지하는 것을 말한다.
이벤트 리스너, 이벤트 핸들러가 바로 이벤트를 발생할 때, 처리하는 함수이며
자주 사용하는 DOM 이벤트로 click, contextmenu, keydown, keyup, focus등이 있다.
앞서 말한 이벤트를 발생시키기 위해서는 이벤트 등록이라는 절차가 필요하며, 대표적인 함수가 addEventListener() 웹 API이다.
예제 코드를 살펴보자 👇
<input type="checkbox" id="item">
let input = document.querySelector('input');
input.addEventListener('click', function(){
alert('clicked');
})
checkbox의 타입을 가지고 있는 input 태그에 'click'이라는 이벤트를 추가하고, 해당 태그를 선택할 때, 브라우저가 이벤트를 감지하고 alert( )창을 띄워준다.
어떻게 브라우저가 이벤트를 감지할 수 있는 걸까??
특정 화면 요소에서 이벤트가 발생했을 때, 해당 이벤트가 더
상위의 화면 요소들로 전달되어 가는 특성을 의마한다.
상위의 화면요소란?
예제 코드로 이를 파악해보자👇
<body>
<div class="one">
<div class="two">
<div class="three">
</div>
</div>
</div>
</body>
let divs = document.querySelectorAll('div');
divs.forEach(function(div){
div.addEventListener('click', function(e){
console.log(e.currentTarget.className);
});
});
<div class="three"><div>을 선택하면, three -> two -> one으로 콘솔에 로그가 찍힌다.
이는 브라우저가 이벤트를 감지하는 방식 때문에 발생하는 현상이다.
브라우저는 특정 화면 요소에서 이벤트가 발생했을 때, 그 이벤트를 최상위에 있는 화면 요소까지 전달시키는 특징을 가지고 있다.
만약, three가 아닌 two를 선택했다면, two -> one으로 콘솔에 로그가 찍힐 것이다.
하위요소에서 상위 요소로의 이벤트 전파 방식을
이벤트 버블링이라고 하는 것이다.
이벤트 버블링과 반대로 진행되는 방식이
Event Capturting이다.

예제 코드로 이를 파악해보자👇
<body>
<div class="one">
<div class="two">
<div class="three">
</div>
</div>
</div>
</body>
let divs = document.querySelectorAll('div');
divs.forEach(function(div){
div.addEventListener(
'click',
function(e){
console.log(e.currentTarget.className);
}),
true // default 값은 false입니다.
);
});
addEventListener( )의 마지막 인자로 true를 전달하면, 이벤트 캡처링이 발생한다.
즉, three를 선택했을때, one -> two -> three으로 콘솔에 로그가 찍힌다.
이벤트 버블링과 캡처링을 사용한 개념이 이벤트 위임이다.
하위 요소에 각각 이벤트를 붙이지 않고, 상위 요소에서 하위 요소의 이벤트들을 제어하는 방식이다.
<body>
<h1>이벤트 버블링 / 캡쳐 학습</h1>
<ul class="itemList">
<li>
<input type="checkbox" id="item1" />
<label for="item1">이벤트 버블링 </label>
</li>
<li>
<input type="checkbox" id="item2" />
<label for="item2">이벤트 캡쳐 </label>
</li>
</ul>
</body>
input을 선택했을 때, alert창이 뜨게 하는 코드이다.
let inputs = document.querySelector("input");
inputs.addEventListener("click", function (e) {
alert('clicked');
});
여기서 li 에 input과 label를 추가하자.
let itemList = document.querySelector(".itemList");
let li = document.createElement("li");
let input = document.createElement("input");
let label = document.createElement("label");
let labelText = document.createTextNode("이벤트 위임 학습");
input.setAttribute("type", "checkbox");
input.setAttribute("id", "item3");
label.setAttribute("for", "item3");
label.appendChild(labelText);
li.appendChild(input);
li.appendChild(label);
itemList.appendChild(li);
하지만, 새로 추가한 리스트 아이템에는 클릭 이벤트가 작동되지 않는다.
왜? 인풋 박스에 이벤트 리스너를 추가하는 시점에 리스트 아이템이 두 개이기 때문이다.
이러한 문제를 해결하기 위해서 우리는 이벤트 위임을 사용해야 하는 것이다.
해결한 코드는 아래와 같다.👇
let itemList = document.querySelector(".itemList");
itemList.addEventListener("click", function (e) {
alert('clicked');
});
화면의 모든 인풋 박스에 이벤트 리스너를 추가하는 방식 대신, 인풋 박스의 상위 요소인 ul에 이벤트 리스너를 달아 하위에서 발생한 이벤트를 감지하는 방식을 사용하는 것이다.
다만, 해당코드는 input과 label 둘 다 이벤트를 감지하므로 input 박스의 이벤트와 감지하기 위해서는 event 객체를 이용하는 것이 필요하다.
itemList.addEventListener("click", function (e) {
if (e.target.tagName === "INPUT") {
console.log("clicked");
} else e.preventDefault();
});
target.tagName을 활용하여 이벤트를 발생시키고, label일 경우 이벤트 발생 자체를 막는 e.preventDefault() 메서드를 사용하여 문제를 해결하였다.
| event.target | event.currentTarget (this) |
|---|---|
| 실제 이벤트가 시작된 ‘타깃’ 요소 | ‘현재’ 요소 |
| 버블링이 진행되어도 변하지 않는다. | 현재 실행 중인 핸들러가 할당된 요소를 참조 |
📚 학습할 때, 참고한 자료 📚
📄 예제코드 📄
너무 멋있어요...