자바스크립트의 이벤트는 전파되는 특성을 가지고 있습니다. 이 전파되는 방식에는 상위요소에서 하위요소로 내려가는 Capture
과 하위요소에서 상위요소로 올라가며 전파되는Bubble
이 있습니다.
아래 사이트를 이용해 조금 더 쉽게 익힐 수 있겠습니다.
또한 리액트, 뷰 등의 사용해 개발을 하다보면 이러한 이벤트 버블링, 캡쳐링으로 인해 예상치 못한 장애가 생길 수도 있습니다.
이번 포스팅에서는 이벤트 전파 방법인 Capture
와 Bubble
에 대해 살펴보도록 하겠습니다.
버블링은 표현 그대로 거품처럼 위로 올라간다라고 생각을 할 수 있습니다. 즉 이벤트가 하위요소에서 발생했을 때 하위요소에서 상위요소로 계속 전파가 되게 되는 것입니다.
예시를 통해 공부해보도록 하겠습니다.
<article>
ARTICLE
<div>
DIV
<p>
P
<br />
<button>BUTTON</button>
</p>
</div>
</article>
const article = document.querySelector('article')
const division = document.querySelector('division')
const paragraph = document.querySelector('paragraph')
const button = document.querySelector('button')
article.addEventListener('click', () => window.alert('article'))
division.addEventListener('click', () => window.alert('division'))
paragraph.addEventListener('click', () => window.alert('paragraph'))
button.addEventListener('click', () => window.alert('button'))
버튼을 클릭하면 버튼 -> p -> div -> article 순으로 경고창이 뜨는 것을 확인할 수 있습니다. 이렇게 상위요소에 속한 하위요소를 클릭하는 경우 상위요소까지 타고 올라가며 이벤트 버블링이 일어나게 됩니다.
이 이벤트 버블링을 방지하기 위해서 사용할 수 있는 방법이 있습니다.
button.addEventListener('click', (evnet) => {
event.stopPropagation();
window.alert('button')
})
event
객체의 메서드인 stopPropagation()
을 사용하면 해당 요소의 이벤트버블링을 막을 수 있습니다.
이벤트 캡쳐링은addEventLstener
의 세번째 매개변수의 capture
옵션을 true 로 설정하면 이벤트 버블링과 반대 방향으로 탐색하게됩니다. 이렇게 상위요소에서 하위요소까지 내려가는 전파방식을 이벤트 캡쳐링이라고 합니다.
<div>
<h1>장바구니</h1>
<ul>
<li>노트북</li>
<li>모니터</li>
<li>책상</li>
<li>의자</li>
<li>키보드</li>
<li>마우스</li>
</ul>
</div>
const container = document.querySelector('div')
const list = document.querySelector('ul')
container.addEventListener('click', (e) => {
console.error('div에 이벤트 발생')
e.target.style.background = 'blue'
})
list.addEventListener('click', (e) => {
console.warn('UL 이벤트 발생')
e.target.style.background = 'red'
})
실습을 해보면 하위요소를 클릭하면 콘솔창에 'UL 이벤트 발생'라는 warn이 출력된 후 'div에 이벤트 발생'라는 에러가 뜨며 이벤트 버블링이 일어나고 있는 것을 활인할 수 있습니다.
이 단계에서 addEvnetLister
의 세번째 매개변수의 Capture
옵션을 활용하면 이벤트 캡쳐링을 확인할 수 있습니다. 이 옵션은boolean
으로 값을 넘기며 기본 값이 false
이기 때문에 true
값을 넣어 확인을 해보도록 하겠습니다.
container.addEventListener('click', (e) => {
console.error('div에 이벤트 발생')
e.target.style.background = 'blue'
}, true)
하위요소를 클릭한 후 상위요소에 관련한 이벤트가 먼저 출력된 후 하위요소의 이벤트가 출력되며 상위요소에서 하위요소로 이벤트가 전파되고 있는 것을 확인할 수 있습니다.
또한 이벤트 옵션은 다양하게 있기 때문에 Capture
옵션을 객체 형태로 작성할 수도 있습니다.
container.addEventListener('click', (e) => {
console.error('div에 이벤트 발생')
e.target.style.background = 'blue'
},{
capture: false
})
이벤트 위임이란 하위 요소마다 이벤트를 붙이지 않고 상위 요소에서 하위 요소의 이벤트들을 제어하는 방식을 말합니다.
예시를 통해 알아보도록 하겠습니다.
만약 list를 클릭했을 때 이벤트가 발생을 하게 하려면, 어디에 이벤트를 달아야할까요?
이벤트를 다루는데 익숙하지 못한 경우 querySelectorAll
을 통해 모든li
를 가지고와 이벤트를 다루려고 할 수도 있습니다.
const items = document.querySelectorAll('li')
items.forEach((item) => item.addEvntListener('click', console.log)
하지만 이 경우 li
가 많아지게 되면 그만큼 EventListener
이 많이 달리며 메모리 낭비가 됩니다.
이러한 경우를 줄이기 위해 이벤트 위임이라는 방법을 이용할 수 있습니다. 이벤트 위임에는 class
, nodeName
을 이용하는 등 다양한 방법이 있습니다.
nodeName
을 사용해 예시를 작성해보도록 하겠습니다.
const items = document.querySelector('ul');
items.addEventListener('click', (event) => {
if(event.target.nodeName === "LI"){
alert('LI Click');
} else {
alert('No LI Click')
}
})
list
를 클릭 했을 경우 알림창에 'LI Click'가 list
가 아닌 부분을 클릭한 경우 알림창에 'No LI Click'가 출력되는 것을 확인할 수 있습니다.
또한 이렇게 이벤트 위임을 활용하면 코드를 재활용해 간단하게 작동을 바꿔볼 수도 있습니다.
const items = document.querySelector('ul');
items.addEventListener('click', (event) => {
if (event.target.nodeName === "LI") {
event.target.style.background = 'blue';
}
})
이번에는 class
를 이용해 분기를 할 수 있습니다. li
에 class="item"
을 정의한 후 예제를 작성 해보도록 하겠습니다.
const items = document.querySelector('ul');
items.addEventListener('click', (event) => {
const [item] = event.target.classList;
//cont item = event.target.classList[0]과 같습니다.
if(item==='item'){
event.target.style.background = 'blue';
}
})
이렇게 classList
에 item
이 있는 경우에만 실행을 하게 할 수 있습니다.
이렇게 이벤트 위임을 사용하는 경우 각 요소마다 이벤트를 달지 않아도 되고, 재활용이 가능하기에 효율적으로 활용을 할 수 있습니다.