이벤트 흐름 (캡쳐링과 버블링)

jude·2022년 2월 11일
0

javascript

목록 보기
3/4
post-thumbnail

이벤트 흐름

<!-- 설명을 위한 구조 간략화 -->

<!-- 최상위는 document -->
<html>
  <body>
    <div class="A1"> <!-- royalblue -->
      <div class="B1"> <!-- red -->
        <div class="C1"></div> <!-- orange -->
      </div>
    </div>
  </body>
</html>

화면은 아래와 같다.

이벤트 흐름은 크게 3가지가 있다.
1. 캡쳐링 단계
2. 타겟 단계
3. 버블링 단계

이벤트 흐름 상세

만약 .B1을 클릭해서 클릭 이벤트를 발생시켰다면, 최상위 요소부터 이벤트 흐름이 발생하여 내려가는데, document, html, body, A1까지가 캡쳐링 단계이고, 클릭한 B1은 타겟 단계, 다시 최상위 까지 버블링 단계로 A1, body, html, document 순으로 이벤트가 흘러간다.

일반적으로 이벤트 리스너타겟 단계버블링 단계에 붙일 수 있는데, 캡쳐링 단계에 이벤트를 등록하고 싶다면 이벤트 리스너의 3번째 인자에 true 값을 넣어주면 된다.

이벤트 버블링

아래와 같이 이벤트를 등록하고, 화면에서 빨간색 B1을 클릭하면 콘솔에는 어떤 결과가 나올까?

const A1 = document.querySelector('.A1')
A1.addEventListener('click', function(e) {
  console.log('A');
})

const B1 = document.querySelector('.B1')
B1.addEventListener('click', function(e) {
  console.log('B');
})

결과는, 클릭한 B1이 먼저, 부모 요소인 A1이 나중에 콘솔이 찍혔다.

// B
// A

클릭한 타깃은 B1인데 이벤트 버블링으로 인해 부모 요소쪽으로 이벤트가 흘러서 A1 요소에 등록되어있던 이벤트 리스너가 이벤트 흐름 순서대로 나중에 콘솔이 찍힌 것이다.

실무에서 자식 요소와 부모 요소에 각기 다른 이벤트를 걸어서 사용을 해야할 때가 있는데, 이와 같은 상황이 되면 머리가 아파온다.

내가 클릭한 요소의 이벤트만 작동 시키고 싶은데 부모 요소의 이벤트까지 같이 작동되버리니까 말이다.
이럴 때 사용하는 것이 이벤트 흐름을 차단 시키는 Event.stopPropagation() 이다.

Event.stopPropagation()

const A1 = document.querySelector('.A1')
A1.addEventListener('click', function(e) {
  console.log('A');
})

const B1 = document.querySelector('.B1')
B1.addEventListener('click', function(e) {
  e.stopPropagation()
  console.log('B');
})

위와 같이 B1에 등록한 이벤트 리스너(타깃, 버블링단계에 등록한) 매개변수로 넘어오는, 이벤트 객체의 메서드인 stopPropagation()을 호출하면 이후의 이벤트 흐름(버블링)을 막을 수 있다.

동일하게 B1을 클릭해도 A1 쪽에 등록한 이벤트 리스너가 작동하지 않는다.

// B

이벤트 캡쳐링

반대로 이벤트 캡쳐링은 실무에서 쓸 일이 그렇게 많지는 않을거 같다.
타깃 요소의 이벤트 실행 전에 부묘 요소에 있는 이벤트를 먼저 실행시켜야 할 경우, 부모 요소의 이벤트를 캡쳐링 단계에 등록한다.

const A1 = document.querySelector('.A1')
A1.addEventListener('click', function(e) {
  console.log('A');
}, true) // 3번째 인자에 true 값을 넣어 캡쳐링 단계에 이벤트를 등록한다.

const B1 = document.querySelector('.B1')
B1.addEventListener('click', function(e) {
  console.log('B');
})

B1을 클릭하면 A1(캡쳐링 단계)에 등록되어 있던 이벤트가 먼저 실행된다.

// A
// B

Event.currentTarget, Event.target

currentTarget은 이벤트가 발생 했을 때, 이벤트가 흘러가는 모든 요소가 각각 currentTarget이 된다.
마찬가지로 이벤트 객체의 프로퍼티로 확인할 수 있다.

target은 이벤트를 발생 시킨 대상 요소이다.

이벤트가 발생되면 document 객체로부터 이벤트 흐름이 시작되는데 시작하자마자 모든 정보를 담고 이벤트가 흐르기 시작한다.
정말 그럴까? 확인해보자.

B1을 클릭했을 때 이벤트 흐름이 아직 B1에 닿지도 않았을 A1 요소에 캡쳐링으로 targetcurrentTarget을 확인해보자.

const A1 = document.querySelector('.A1')
A1.addEventListener('click', function(e) {
  console.log(e.currentTarget);
  console.log(e.target);
  console.log('A');
}, true) // 3번째 인자에 true 값을 넣어 캡쳐링 단계에 이벤트를 등록한다.

const B1 = document.querySelector('.B1')
B1.addEventListener('click', function(e) {
  console.log('B');
})

// console

// <div class="A1">...</div>
// <div class="B1">...</div>
// A
// B

결과는 위와 같이 A1 요소에서도 target의 정보를 확인할 수 있다.

이러한 currentTargettarget 정보를 활용하여, 자식 요소인 B1, C1을 클릭 했을 때는 A1 요소에 등록한 이벤트가 작동하지 않고, 화면에서 파란색으로 보이는 A1 영역만 클릭 했을 때 이벤트가 발생하도록 할 수 있다.

const A1 = document.querySelector('.A1')
A1.addEventListener('click', function(e) {
  if (e.target === e.currentTarget) {
    console.log('A');
  }
})

이렇게 코드를 짜놓으면 화면에서 눈으로 보이는 A1 요소의 파란색 영역을 클릭 했을 때만 콘솔 ‘A’가 찍히게 된다.

profile
UI 화면 만드는걸 좋아하는 UI개발자입니다. 프론트엔드 개발 공부 중입니다. 공부한 부분을 블로그로 간략히 정리하는 편입니다.

0개의 댓글