// html 구조
<div id="ancestor">
<div id="parent">
<div id="child"></div>
</div>
</div>
// javascript 구조
ancestor.addEventListener("click", (e) => {
console.log('ancestor clicked');
})
parent.addEventListener("click", (e) => {
console.log('parent clicked');
})
child.addEventListener("click", (e) => {
console.log('child clicked');
})
위 코드를 작성하면 무슨일이 일어날까
조상 요소 클릭 -> 조상 요소 이벤트 발생
부모 요소 클릭 -> 부모 요소 이벤트 발생 & 조상 요소 이벤트 발생
자식 요소 클릭 -> 자식 요소 이벤트 발생 & 부모 요소 이벤트 발생 & 조상 요소 이벤트 발생
자식 요소만 이벤트 발생을 원했지만 부모, 조상 요소까지 이벤트가 발생했다.
이때 자식 요소를 '타겟' 요소라 하고
부모, 조상 요소까지 이벤트가 전파 된 것을 '버블링'이라고 한다.
단, 이벤트가 전파되는 조건은 같은 이벤트일 경우다
위 그림처럼 타겟 요소부터 부모, 조상이어지면서 DOM구조의 최상단까지 버블링이 진행된다.
반대로 타겟 요소에 이벤트가 발생했을 때,
DOM구조의 시작 요소인 window부터 타겟 요소까지 이벤트가 전파되는 것을 '캡처링'이라 한다.
다만 캡처링은 실무에서 크게 쓰이지는 않지만 발생했을 때의 원인을 알아야하기 때문에 알고는 있어야한다.
이벤트 코드를 보면 핸들링, 핸들러, 캡처링 사용 유무 순으로 인자값을 가진다.
element.addEventListener('click', (e) => { ... }, true);
element.addEventListener('click', (e) => { ... }, {capture: true});
이벤트 코드의 3번째 인자값은 선택이며, 없을 시 캡처링은 false 값을 버블링은 true 값을 가진다.
즉, 버블링은 디폴트 값으로 항상 진행 된다고 보면 된다.
만약 버블링을 막고 싶다면 이벤트 코드 마지막에 멈추는 코드를 추가적으로 작성하면 된다.
하지만 버블링이 막힌 타겟 요소는 부모 이상의 요소에서 추가적인 다른 이벤트를 발생하고 싶을 때
영역이 잡히지 않으니 추천하지 않는다.
element.addEventListener("click", (e) => {
//동작 코드
e.stopPropagation() // 이벤트 전파 방지
})
버블링이나 캡처링을 막기 위해서는 코드를 작성할 때 미리 설계를 잘해야 한다.
실제로 버블링이 쉽게 생기지는 않는다.
반대로 버블링이 필요한 경우도 있다.
예를 들면 리스트와 세부사항이 있을 때, 세부 사항 각각에 이벤트를 작성하기에는 중복되는 코드가 많아지기 때문에 부모 요소인 리스트에 이벤트를 위임 시켜 하나의 이벤트로 세부 사항 모두에게 이벤트를 동작하게 만들 수 있다.
list.addEventListener('click', function(e) {
if (e.target.classList.contains('item')) {
e.target.classList.toggle('done');
}
});
위임을 했을 때 리스트를 선택하게 되면 전체 영역의 이벤트가 발생하기 때문에 조건문을 통해
세부 사항 요소만 작동하게 만들어 줘야 한다.
이벤트 위임의 장점은 코드를 줄일 수 있고, 성능 또한 좋아진다는 점이다.
- 이벤트 발생 시, 상위로 올라가면서 같은 이벤트가 전파된다 (버블링)
- 반대의 경우, 최상단 window부터 타겟까지 이벤트가 발생한다 (캡처링)
- 이를 통해 이벤트 위임이 가능하다.