우리가 웹페이지에서 버튼을 클릭할 때 어떤 상호작용을 일으키는 기능을 만들었다고 해보자.
사람 눈에는 안 보이지만 웹페이지는 render tree에 따라서 자여지 여러개의 층으로 나누어져 있다. 이 층에서 어떻게 정확히 내가 클릭한 버튼에 기능을 부여할 수 있을까?
우리가 어떤 버튼을 클릭하는 화면을 만들었다고 했을 때 그 화면의 보이지 않는 Dom tree의 모습은 다음과 같다.
사실 우리가 버튼을 클릭했다고 버튼만 눌리는 것이 아니라 모든 상위 계층이 다 눌리게 되는 것이다.
버튼 클릭시 브라우저는 window 노드부터 dom 요소들을 타고 버튼을 찾으러 내려간다. 이 과정이 바로 캡처링이다.
이 버튼에 등록된 이벤트는 다시 부모요소를 타고 window까지 올라가게 되는데 이 과정이 버블링이다.
이 두가지 작용 때문에 이벤트를 등록하고 실행하면 그 이벤트는 항상 두번 불려지게 된다.
우리는 버블링과 캡처링 중 한단계에만 이벤트를 등록할 수도 있다.
addEventListener로 event를 등록할때 3번째 인자로 boolean 값을 넣어줌으로써 event가 발생하는 시점을 조절할 수 있다.
//예시
addEventListener("click", fucntion(){console.log("Capturing")},true) --> 캡처링
addEventListener("click", fucntion(){console.log("Bubbling")},false) --> 버블링
위 방법말고도 event가 버블링 되는 것을 막을 수 있는 방법이 있다.
funtion에 event를 받아오고 event에 stopPropagation()를 등록하면 된다.
addEventListener("click", (event) => stopBubbling(event))
funtion stopBubbling(event){
event.stopPropagation()
console.log("Now Bubbling stopped")
}
event.stopImmediatePropagation();
위 stopPropagation()과 비슷하나 같은 요소에 여러개의 이벤트를 등록했을때 사용한다.
버블링의 특징을 이용하여 다수의 요소에 event를 일일히 등록하는 것이 아닌 상위요소에만 등록하여 번거로움도 없애고 속도도 향상시키는 방법이다.
<ul>
<li>
<h2>Item 1</h2>
<p>Some text</p>
</li>
<li>
<h2>Item 2</h2>
<p>Some text</p>
</li>
.
.
.
</ul>
여기서 li 요소를 클릭할 때 색깔이 바뀌는 기능을 넣고 싶다면 일반적으로
const listItems = document.querySelectorAll("li");
listItems.forEach((listItem) => {
listItem.addEventListener("click", (e) => {
e.target.classList.toggle("highlight");
});
});
처럼 li 요소들에 각각 이벤트를 등록 할 수 있다. 하지만 이 방법은 js의 속도를 느리게 하는 원인이 될 수 있다.
이를 해결하기위해 event delegation 이벤트 위임을 사용하면 다음과 같다.
const list = document.querySelector("ul");
list.addEventListener("click", (e) => {
e.target.closest("li").classList.toggle("highlight");
form.click();
});
부모요소인 Ul에 이벤트를 등록하는데 가장 가까운 li요소의 색이 변하도록 하는 함수로 기능을 만들 수도 있는 것이다.
여기서 target과 currentTarget을 잘 보고 등록해야 원치 않는 동작을 막을 수 있다.
target : 이벤트가 발생한 요소를 반환
currentTarget: 이벤트를 등록해놓은 요소를 반환
이벤트 위임을 적절히 사용하는 것이 효율적인 코드를 작성하는 것이라 할 수 있다.