이벤트 버블링(Event Bubbling)
한 요소에 이벤트가 발생하면, 이 요소에 할당된 핸들러가 동작하고, 이어서 부모 요소의 핸들러가 동작한다. 가장 최상단의 조상 요소를 만날 때까지 이 과정이 반복되면서 요소 각각에 할당된 핸들러가 동작한다.
이벤트 버블링은 한 요소에서 이벤트가 발생했을 때 해당 이벤트가 이 요소의 상위 요소로 전달되어가는 특성을 말한다.
<style>
body * {
margin: 10px;
border: 1px solid blue;
}
</style>
<form onclick="alert('form')">FORM
<div onclick="alert('div')">DIV
<p onclick="alert('p')">P</p>
</div>
</form>
위 예제에서 가장 안쪽의 P를 누르게 되면 다음과 같이 작동한다.
- <p>에 할당된 onclick 핸들러가 동작( alert('p') )
- 바깥의 <div>에 할당된 핸들러가 동작( alert('div') )
- 그 바깥의 <form>에 할당된 핸들러가 동작( alert('form') )
- document 객체를 만날 때까지, 각 요소에 할당된 onclick 핸들러가 동작한다.
p 태그 한 개만 클릭했을 뿐인데 3개의 이벤트가 발생되는 이유는 브라우저가 이벤트를 감지하는 방식때문이다. 브라우저는 특정 화면 요소에서 이벤트가 발생했을 때 그 이벤트를 최상위에 있는 화면 요소까지 이벤트를 전파시킨다. 그동안 잘 감지하지 못했지만 거의 모든 이벤트는 버블링 된다. 주어진 예시에서는 각 태그마다 이벤트가 등록되어 있기 때문에 버블링을 확인할 수 있는 것뿐이다.
e.target
이벤트가 발생한 가장 안쪽의 요소는 타깃(target)요소라고 불리고, event.target을 사용해 접근할 수 있다. 이를 통해 이벤트가 정확히 어디서 발생했는지를 확인할 수 있다.
<style>
body * {
margin: 10px;
border: 1px solid blue;
}
</style>
<form>FORM
<div>DIV
<p>P</p>
</div>
</form>
<script>
document.querySelector('form').addEventListener('click', (e)=>{
alert(`FORM ${e.target.tagName}`);
})
document.querySelector('div').addEventListener('click', (e)=>{
alert(`DIV ${e.target.tagName}`);
})
document.querySelector('p').addEventListener('click', (e)=>{
alert(`P ${e.target.tagName}`);
})
</script>
다음과 같은 예시에서 P를 누르면 P P -> DIV P -> FORM P 순으로 출력이 된다. 버블링이 일어나는 과정에서 정확한 이벤트가 어디서 일어나는지를 e.target을 사용하여 확인할 수 있다. (e.target은 HTML element를 리턴한다)
버블링 중단
이벤트 객체의 메서드인 event.stopPropagation()를 사용하면 버블링을 중단할 수 있다. 하지만 이벤트 버블링을 막아야 하는 경우는 거의 없다.
이벤트 캡처링
이벤트 캡쳐링은 이벤트가 하위 요소로 전파되는 단계로 이벤트 버블링과 반대 방향으로 진행되는 이벤트 전파 방식이다. 이벤트가 최상위 조상에서 시작해 아래로 전파되며, 타깃 요소에 도착하기까지의 단계이다.
캡처링 단계에서 이벤트를 확인하려면 addEventListener의 capture 옵션을 true로 설정해야 한다.
- 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 (버블링 단계, 두 번째 리스너)
참고
https://javascript.plainenglish.io/the-3-phases-of-event-propagation-explained-f76348b5343f
https://ko.javascript.info/bubbling-and-capturing
'JavaScript' 카테고리의 다른 글
[Javascript] 상속(Inheritance) (0) | 2021.10.19 |
---|---|
[Javascript] Class (0) | 2021.10.18 |
[Javascript] DOM 노드 (1) | 2021.10.11 |
[Javascript] for of, forEach, map 비교 (0) | 2021.09.29 |
[Javascript] Optional Chaining (0) | 2021.09.23 |