지난 포스트에서 이벤트 버블링(bubbling)에 대해 소개한 바 있다.
이벤트에는 버블링 외에도 캡처링(capturing)이라는 흐름이 존재한다. 실제 코드에서는 자주 쓰이지 않지만 종종 유용한 경우가 있으니 알아보도록 하자.
cf) 캡처링 단계를 이용해야 하는 경우는 흔치 않기 때문에, 캡처링에 관한 코드를 발견하는 일은 거의 없을지도 모른다.
표준 DOM 이벤트에서 정의한 이벤트 흐름에는 아래의 3가지 단계가 있다.
- 캡처링 단계: 이벤트가 하위 요소로 전파되는 단계
- 타깃 단계: 이벤트가 실제 타깃 요소에 전달되는 단계
- 버블링 단계: 이벤트가 상위 요소로 전파되는 단계
<table> 안의 <td>를 클릭하면 어떻게 이벤트가 흐르는지 아래 그림을 통해 이해해 보자.
<td>를 클릭하면 이벤트가 최상위 조상에서 시작해 아래로 전파되고(캡처링 단계), 이벤트가 타깃 요소에 도착해 실행된 후(타깃 단계), 다시 위로 전파된다(버블링 단계). 이러한 과정을 통해 요소에 할당된 이벤트 핸들러가 호출되는 것이다.
on<event> 프로퍼티나 HTML 속성, addEventListener(event, handler)를 이용해 할당된 핸들러는 캡처링에 대해 전혀 알 수 없다. 이 핸들러들은 두 번째 혹은 세 번째 단계의 이벤트 흐름(타깃 단계 또는 버블링 단계) 에서만 동작한다. 따라서 캡처링 단계에서 이벤트를 잡아내려면 아래와 같이 addEventListener의 capture 옵션을 true로 설정해야 한다.
// addEventListener의 capture 옵션을 true로 설정
elem.addEventListener(..., {capture: true})
// 또는 아래와 같이 {capture: true} 대신 true를 써 줘도 됨
elem.addEventListener(..., true)
이러한 capture 옵션은 두 가지 값을 가질 수 있는데 false면 핸들러는 버블링 단계에서 동작하게 되고(default 값), true면 핸들러는 캡처링 단계에서 동작한다.
참고로 위와 같이 capture 옵션을 설정하여 핸들러를 할당해 줬다면 핸들러를 제거할 때에도 removeEventListener(..., true)와 같이 지워야 한다. 같은 단계에 있어야 핸들러가 지워진다.
공식적으로는 총 3개의 이벤트 흐름이 있지만 이벤트가 실제 타깃 요소에 전달되는 단계인 타깃 단계는 별도로 처리되지 않는다. 캡처링과 버블링 단계의 핸들러는 타깃 단계에서 트리거된다.
아래 예를 통해 캡처링과 버블링에 대해 살펴 보자.
<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> (버블링 단계, 두 번째 리스너)
event.eventPhase 프로퍼티를 이용하면 현재 발생 중인 이벤트 흐름의 단계를 알 수 있다. 반환되는 정수 값에 따라 이벤트 흐름의 현재 실행 단계를 구분할 수 있는 것. 하지만 핸들러를 통해 흐름 단계를 알 수 있기 때문에 이 프로퍼티는 자주 사용되지 않는다.
'Javascript > Javascript' 카테고리의 다른 글
[Javascript] XMLHttpRequest로 AJAX 요청하기 (0) | 2024.10.25 |
---|---|
[Javascript] console 사용 시 쉼표(,)와 +의 차이 (0) | 2024.10.22 |
[Javascript] Event 객체 (0) | 2024.10.18 |
[Javascript] 이벤트 핸들러와 this (0) | 2024.10.16 |
[Javascript] Event의 종류 (2) | 2024.10.15 |