[ 버블링 ]
한 요소에 이벤트가 발생하면 이 요소에 할당된 핸들러가 동작하고 이어서 부모 요소의 핸들러가 동작하며 가장 최상단의 조상 요소를 만날때까지 이 과정이 반복되면서 요소 각각에 할당된 핸들러가 동작함
즉 이벤트 버블링은 HTML에서 이벤트가 발생했을 때, 해당 요소에서 이벤트가 처리된 후 상위 요소로 이벤트가 전파되는 현상을 의미
▶ 예시
<form onclick="alert('form')">FORM
<div onclick="alert('div')">DIV
<p onclick="alert('p')">P</p>
</div>
</form>
가장 안쪽의 p태크를 클릭하게 되면
- <p>에 할당된 onclick 핸들러가 동작
- 바깥의 <div>에 할당된 핸들러가 동작
- 그 바깥의 <form>에 할당된 핸들러가 동작
- document 객체를 만날 때까지, 각 요소에 할당된 onclick 핸들러가 동작
p태그를 누르게 되면 p → div → form 순서로 3개의 얼럿 창이 뜨게 됨
▶ 버블링의 이벤트 위임
이벤트 버블링은 이벤트가 전파되는 동안 부모 요소에서도 이를 감지할 수 있다는 장점이 있음
이벤트를 처리하는 핸들러 함수를 부모 요소에 등록하면 자식 요소에서 발생한 이벤트도 모두 처리할 수 있음
이를 활용하여 여러 하위 요소에서 발생하는 이벤트를 하나의 이벤트 핸들러로 바인딩하는 이벤트 위임 처리를 할 수 있음
- 이벤트위임(Event Delegation)
하위 요소에 각각 이벤트를 붙이지 않고 상위 요소에서 하위 요소의 이벤트를 제어하는 방식
이벤트 위임을 활용하면 다수의 이벤트 핸들러를 할당하는 대신 하나의 이벤트 핸들러만 할당하기 때문에 코드가 단순해지고 메모리가 절약됨 또한 요소가 추가되거나 제거되는 동작이 많은 경우에도 짧은 코드만으로 이벤트 처리가 가능함
<ul>
<li>first</li>
<li>second</li>
<li>third</li>
</ul>
const list = document.querySelector('ul');
list.addEventListener("click", (e) => {
if (e.target.tagName === "LI") {
console.log(e.target.innerText);
}
});
li에 일일이 이벤트 핸들러를 할당하는 대신에 li의 공통 부모 요소인 ul 태그에 하나의 이벤트 핸들러를 할당
이벤트 버블링으로 인해 li에서 발생한 이벤트가 ul로 전파되기 때문에 li에서 발생한 이벤트를 상위 요소 ul에서 감지하고 처리할 수 있음 따라서 해당 예시 코드의 li를 클릭하면 클릭한 li의 텍스트가 콘솔에 출력됨
▶ 버블링 방지하기
이벤트 버블링은 타깃 이벤트에서 시작해서 <html> 요소를 거쳐 document 객체를 만날 때까지 각 노드에서 모두 발생하며 몇몇 이벤트는 window 객체까지 거슬러 올라가기도 함. 이 때도 모든 핸들러가 호출됨
버블링을 방지하기 위해서는 이벤트 핸들러에서 이벤트가 발생한 요소를 확인하여 처리하거나 이벤트 객체의 메서드인 event.stopPropagation()를 사용하면 핸들러에게 이벤트를 완전히 처리하고 난 후 버블링을 중단하도록 명령할 수도 있음
<body onclick="alert(`버블링은 여기까지 도달하지 못함`)">
<button onclick="event.stopPropagation()">클릭해 주세요.</button>
</body>
버튼을 클릭하게 되더라도 alert창은 뜨지 않음
[ 캡쳐링 ]
버블링과 반대 방향으로 진행되는 이벤트 전파 방식임
표준 DOM 이벤트에서 정의한 이벤트 흐름엔 3가지 단계가 있음
https://ko.javascript.info/bubbling-and-capturing
- 캡처링 단계 – 이벤트가 하위 요소로 전파되는 단계
- 타깃 단계 – 이벤트가 실제 타깃 요소에 전달되는 단계
- 버블링 단계 – 이벤트가 상위 요소로 전파되는 단계
on<event> 프로퍼티나 HTML 속성, addEventListener(event, handler)를 이용해 할당된 핸들러는 캡처링에 대해 전혀 알 수 없음. 이 핸들러들은 두 번째 혹은 세 번째 단계의 이벤트 흐름(타깃 단계와 버블링 단계)에서만 동작함
캡처링 단계에서 이벤트를 잡아내려면 addEventListener의 capture 옵션을 true로 설정해야 함
elem.addEventListener(..., {capture: true})
// 아니면, 아래 같이 {capture: true} 대신, true를 써도 됨
elem.addEventListener(..., true)
capture 옵션은 두 가지 값을 가질 수 있음
- false이면(default 값) 핸들러는 버블링 단계에서 동작
- true이면 핸들러는 캡처링 단계에서 동작
▶ 예시
<style>
body * {
margin: 10px;
border: 1px solid blue;
}
</style>
<form>FORM
<div>DIV
<p>P</p>
</div>
</form>
<script>
for(let elem of document.querySelectorAll('*')) {
elem.addEventListener("click", e => alert(`캡쳐링: ${elem.tagName}`), true);
elem.addEventListener("click", e => alert(`버블링: ${elem.tagName}`));
}
</script>
<p>를 클릭하면 다음과 같은 순서로 이벤트가 전달됨
- HTML → BODY → FORM → DIV (캡처링 단계, 첫 번째 리스너)
- P (타깃 단계, 캡쳐링과 버블링 둘 다에 리스너를 설정했기 때문에 두 번 호출됨)
- DIV → FORM → BODY → HTML (버블링 단계, 두 번째 리스너)
'CS > 프로그래밍' 카테고리의 다른 글
동기, 비동기 (1) | 2023.12.15 |
---|---|
Typescript (0) | 2023.12.08 |
클로저 (1) | 2023.12.07 |
this (2) | 2023.12.07 |
Flex 속성(CSS) (0) | 2023.12.01 |