[JS] 이벤트 버블링(bubbling)과 캡처링(capturing)

토끼는 개발개발·2023년 8월 5일
0

JavaScrpit

목록 보기
2/2
post-thumbnail
post-custom-banner

들어가기에 앞서 먼저 예시부터 살펴보자.

<form onclick="alert('form')">FORM
  <div onclick="alert('div')">DIV
    <p onclick="alert('p')">P</p>
  </div>
</form>

이와 같은 화면에서 <p>를 클릭하면 어떻게 될까?
<p><div><form> 순서로 3개의 얼럿 창이 뜰 것이다.

왜 이런 현상이 일어나는 것일까?
<p>를 클릭하면, <p>의 얼럿창만 뜨도록 하려면 어떻게 해야할까?

이를 해결하려면 우리는 버블링과 캡처링의 개념을 알아야 한다.



📌 버블링(bubbling)

1. 버블링이란?

한 요소에 이벤트가 발생하면, 이 요소에 할당된 핸들러가 동작하고, 이어서 부모 요소의 핸들러가 동작한다. 가장 최상단의 조상 요소를 만날 때까지 이 과정이 반복되면서 요소 각각에 할당된 핸들러가 동작한다.

한 마디로, 이벤트가 실행된 요소부터 부모요소까지 거슬러 올라가며 핸들러들이 동작하는 것이다.

위의 예시를 통해 버블링의 순서를 정리해 보자.

<form onclick="alert('form')">FORM
  <div onclick="alert('div')">DIV
    <p onclick="alert('p')">P</p>
  </div>
</form>

가장 안쪽의 <p>를 클릭하면 다음 순서와 같이 동작한다.

1) <p>에 할당된 onclick 핸들러가 동작
2) <div>에 할당된 핸들러가 동작
3) <form>에 할당된 핸들러가 동작
4) document 객체를 만날 때까지, 각 요소에 할당된 onclick 핸들러가 동작

이런 동작 방식 때문에 <p> 요소를 클릭하면 p → div → form 순서로 3개의 얼럿 창이 뜨는것이다.


이런 흐름을 '이벤트 버블링’ 이라고 부른다.
이벤트가 제일 깊은 곳에 있는 요소에서 시작해 부모 요소를 거슬러 올라가며 발생하는 모양이 마치 물속 거품(bubble)과 닮았기 때문이다.

거의 모든 이벤트는 버블링 된다. (focus 이벤트와 같이 버블링 되지 않는 이벤트도 있다.)



2. 버블링 중단 (이벤트 전파 차단)

stopPropagation()
: 핸들러에게 이벤트를 완전히 처리하고 난 후 버블링을 중단하도록 명령하는 함수.

<body onclick="alert(`버블링은 여기까지 도달하지 못합니다.`)">
  <button onclick="event.stopPropagation()">클릭해 주세요.</button>
</body>

이 경우, 버튼을 클릭해도 body.onclick까지 도달하지 않는다.




📌 캡처링(capturing)

캡처링이란?

캡처링은 버블링과 반대되는 개념으로, 최상위 태그에서 해당 태그를 찾아 내려간다.

캡처링 단계를 이용해야하는 경우, addEventListener(event, handler)에서capture:true를 설정해주면 된다.

<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>를 클릭하면 다음과 같은 순서로 이벤트가 전달된다.

1) HTML → BODY → FORM → DIV (캡처링 단계, 첫 번째 리스너)
2) P (타깃 단계, 캡쳐링과 버블링 둘 다에 리스너를 설정했기 때문에 두 번 호출된다.)
3) DIV → FORM → BODY → HTML (버블링 단계, 두 번째 리스너)

위와 같이 표준 DOM 이벤트에서 정의한 이벤트 흐름엔 3가지 단계가 있다.

캡처링 단계 – 이벤트가 하위 요소로 전파되는 단계
타깃 단계 – 이벤트가 실제 타깃 요소에 전달되는 단계
버블링 단계 – 이벤트가 상위 요소로 전파되는 단계

event.eventPhase를 찍어보면 각각의 실행단계가 출력된다.




📌 event.target / event.currentTarget(=this)

  • event.target – 이벤트가 발생한 가장 안쪽의 요소
  • event.currentTarget (=this) – 이벤트를 핸들링 하는 현재 요소
    (핸들러가 실제 할당된 요소)

예시를 살펴보자.

<li>
  <button @click="onClick()">
    <span>Click!</span>
  </button>
</li>
const onClick = (event) => {
	console.log(event.target);
  	console.log(event.currentTarget);
}

결과는 다음과 같다.

<span>Click!</span>
---
<button>
	<span>Click!</span>
</button>

즉, event.target은 내가 실제 클릭한 자식요소를 반환하고 event.currentTarget은 이벤트가 부착된 부모의 위치를 반환한다.




참고문헌

https://ko.javascript.info/bubbling-and-capturing
짐코딩 - 버블링과 캡처링
캡틴판교 - 버블링과 캡처링




한줄쓰기

실무에서 버튼에 팝업오픈 이벤트를 만들다가, 어느 경우에는 팝업이 오픈되고 어느 경우에는 오픈되지 않는 경우가 발생했다.
도대체 왜 어떨때는 되고, 어떨때는 되지않는지 이유를 못찾다가 깨달았었다.
버튼 내의 <span>태그를 클릭하면 오픈되고, <span>이 아닌 영역을 클릭하면 오픈되지 않았던 것이다.
이벤트를 전달할 때, event.currentTarget이 아닌 event.target을 전달했기 때문이다.
그 때가 기억에 남아 버블링 관련한 포스팅에서 해당 내용을 추가해보았다..

profile
하이 이것은 나의 깨지고 부서지는 기록들입니다
post-custom-banner

1개의 댓글

comment-user-thumbnail
2023년 8월 5일

정보에 감사드립니다.

답글 달기