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) |
---|---|
실제 이벤트가 시작된 ‘타깃’ 요소 | ‘현재’ 요소 |
버블링이 진행되어도 변하지 않는다. | 현재 실행 중인 핸들러가 할당된 요소를 참조 |
📚 학습할 때, 참고한 자료 📚
📄 예제코드 📄
너무 멋있어요...