늘 겸손하게

JavaScript - 이벤트 버블링 & 캡쳐링 & 위임 본문

Programming/JavaScript

JavaScript - 이벤트 버블링 & 캡쳐링 & 위임

besforyou999 2022. 8. 10. 17:57

이벤트 버블링

 

 

한 요소에서 이벤트가 발생하면 해당 요소에서부터 해당 요소의 최상단 부모 요소까지 이벤트가 전달되면서 만나는 이벤트 핸들러들이 작동하는것을 버블링(bubbling)이라고 합니다.

 

거품(버블)이 수면 맨 아래에서 위로 올라가는 것처럼 이벤트가 위로 올라가는 것에 빗대어 버블링이라 부릅니다.

 

< 예 >

<body>
  <div class="one">
    One
    <div class="two">
      Two
      <div class="three">
          Three
      </div>
    </div>
  </div>
</body>
const divs = document.querySelectorAll('div')

divs.forEach(ele => {
  ele.addEventListener('click', handler);
})

function handler(event) {
  console.log(event.currentTarget.className);
}

위와 같은 html에서 맨 아래 div인 three를 클릭하면 콘솔에는 아래와 같은 결과가 나옵니다.

 

 

<div class="three">three</div> 에서 발생한 이벤트가 부모인 two, one까지 전달됩니다.

 

이러한 흐름을 '이벤트 버블링'이라고 합니다.

 

 

이벤트 캡쳐링

 

 

이벤트에는 버블링 이외에도 '캡처링(capturing)'이라는 흐름이 존재합니다. 

 

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

 

  1. 캡처링 단계 - 이벤트가 하위 요소를 거쳐 타깃 요소로 전파되는 단계
  2. 타깃 단계 - 이벤트가 실제 타깃 요소에 전달되는 단계
  3. 버블링 단계 - 이벤트가 상위 요소로 전파되는 단계

 

캡처링 단계에서 거쳐가는 이벤트를 이벤트 핸들러로 잡아내려면 addEventListener의 capture 옵션을 true로 설정해야합니다.

 

elem.addEventListener(..., {capture: true})
// 아니면, 아래 같이 {capture: true} 대신, true를 써줘도 됩니다.
elem.addEventListener(..., true)

 

이벤트 버블링과 같은 코드로 예시를 들어보겠습니다.

 

< 예시 >

<body>
  <div class="one">
    One
    <div class="two">
      Two
      <div class="three">
          Three
      </div>
    </div>
  </div>
</body>
const divs = document.querySelectorAll('div')

divs.forEach(ele => {
  ele.addEventListener('click', handler, { capture: true });
})

function handler(event) {
  console.log(event.currentTarget.className);
}

마찬가지로 마지막 div인 three를 클릭하면 아래와 같은 결과가 나타납니다.

 

 

body에서 시작된 이벤트가 첫 번째, 두 번째 div를 거쳐 마지막 div까지 전달됩니다.

 

 

event.stopPropagation()

 

 

이벤트는 상위 요소에 전파되는 성질이 있습니다. 다른 건 다 귀찮고 이벤트가 발생한 요소에서만 이벤트 처리를 하고 싶다면, 다른 요소로 이벤트 전파를 중단시키고 싶은 경우 event.stopPropagation()을 사용합니다.

 

이벤트 버블링에서 사용한 예시 코드를 약간 수정해서 써보겠습니다.

 

<body>
  <div class="one">
    One
    <div class="two">
      Two
      <div class="three">
          Three
      </div>
    </div>
  </div>
</body>
const divs = document.querySelectorAll('div')

divs.forEach(ele => {
  ele.addEventListener('click', handler);
})

function handler(event) {
  console.log(event.currentTarget.className);
  event.stopPropagation();
}

모든 이벤트 핸들러마다 event.stopPropagation() 메소드를 달아주었습니다. 

맨 아래 div를 클릭하면 결과는 아래와 같습니다.

 

 

부모 요소로 이벤트 전파가 중단됩니다.

 

 

이벤트 위임 - Event Delegation

 

 

그렇다면 이벤트 위임은 무엇일까요?

 

한문장으로 요약하면 '이벤트 핸들링을 부모 요소에게 넘기는 일'이라 설명할 수 있습니다.

 

모든 요소에 이벤트 핸들러를 붙이는 일은 번거롭고 메모리 사용도 효율적이지 않으니 이벤트 버블링과 event.stopPropagation을 활용해 상위 요소 한개에서 자식 요소들의 이벤트 핸들링을 맡게 하는 일입니다.

 

코드로 설명해보겠습니다.

 

코드를 간략히 설명하면 Ancestor div > Parent div > 그 밑에 10개의 자식 div가 존재하는 코드입니다.

이벤트 핸들러는 parent class div에만 추가했습니다.

 

<body>
  <div class="Ancestor">
    Ancestor
    <div class="parent">
      parent
      <div class="child">
        child1
      </div>
      <div class="child">
        child2
      </div>
      <div class="child">
        child3
      </div>
      <div class="child">
        child4
      </div>
      <div class="child">
        child5
      </div>
      <div class="child">
        child6
      </div>
      <div class="child">
        child7
      </div>
      <div class="child">
        child8
      </div>
      <div class="child">
        child1
      </div>
      <div class="child">
        child9
      </div>
      <div class="child">
        child10
      </div>
    </div>
  </div>
</body>
const parent = document.querySelector('.parent')

parent.addEventListener('click', handler)

function handler(event) {
  console.log(event.currentTarget.className);
  event.stopPropagation();
}

 

 

어떠한 child를 클릭해도 parent가 콘솔에 출력되는것을 볼 수 있습니다. 하지만 parent에 등록한 이벤트 핸들러 내부에 event.stopPropation() 메소드를 호출하여 Ancestor div까지 이벤트가 전파되지는 않습니다.

 

 

이와 같은 방식으로 모든 요소에 이벤트를 달 필요없이 부모 요소 하나에 이벤트 처리를 넘길 수 있습니다.

 

 

 

출처

https://joshua1988.github.io/web-development/javascript/event-propagation-delegation/

https://ko.javascript.info/bubbling-and-capturing