JS_Bubbling and Capture

이종국·2022년 8월 5일
0

버블링과 캡처란?

버블링과 캡처는 단계(phases)를 설명하는 단어다.

‘여기서 단계란, 어떻게 해당 브라우저가 target된 이벤트(nested elements)들을 다루는 지에 대한 묘사를 일컫는다.

버블링과 캡처링은 텍스트로만 의미를 전달하기 힘드니 예제를 활용해보자.

부모 요소에 listener를 세팅해보자.

<div id="container"><button>Click me!</button></div><pre id="output"></pre>

여기서 버튼은 또 다른 요소인 (div)요소 안에 있다. 이것을 우리는 div요소가 여기서 부모 요소(버튼을 포함하고 있는)라고 말하는 것이다. 만약 우리가 부모 요소에 클릭 이벤트를 추가하고 그런 다음 버튼을 클릭하게 되면 어떤 일이 벌어질까?

const output = document.querySelector('#output');
function handleClick(e) {
  output.textContent += `You clicked on a ${e.currentTarget.tagName} element\n`;
}

const container = document.querySelector('#container');
container.addEventListener('click', handleClick);

사용자가 버튼을 클릭할 때, 우리는 부모(div)가 클릭 이벤트를 발생시키는 것을 볼 수 있다.

📝This makes sense

💡 1 )해당 버튼이 div요소 안에 있구나 2) 그래서 너가 버튼을 클릭할 때 너는 또한 암묵적으로 내부에 있는 요소를 클릭 중임을 알 수 있구나

즉 이 개념이 버블링과 캡처를 알아보기 위한 전제조건이다.

버블링 예제

HTML은 동일하고, 자바스크립트에 이벤트리스너를 다중으로 Body에, container에, button에 등록시켜보자.

const output = document.querySelector("#output");
      function handleClick(e) {
        output.textContent += ` ✏️클릭 이벤트가 발생한 것은 ${e.currentTarget.tagName}이다. \n`;
      }

      const container = document.querySelector("#container");
      const button = document.querySelector("button");

      document.body.addEventListener("click", handleClick);
      container.addEventListener("click", handleClick);
      button.addEventListener("click", handleClick);

버튼을 한 번 클릭 했을 뿐인데. 3가지의 이벤트들이 발생한 것을 볼 수 있을 것이다.

이 경우, 가장 먼저 클릭 된 버튼에 의해서 버튼이 실행될 거고

⇒ 그 다음 부모 요소인 div tag가 따라오고 ⇒ 그리고 더 상위 부모인 body 요소가 따라서 실행되는 것이다.

우리는 이것을 “해당 이벤트는 클릭 된 가장 안쪽 요소로부터 bubbles up(버블링)이 되는 구나”라고 설명할 수 있을 것이다.

즉, 버블링은 가장 내부요소부터 상위 부모로 거슬러 올라간다는 것이다.

이러한 버블링은 때론 유용하게 사용될 수 있기도하고 예기치 못한 문제를 발생하기도 한다.

이제 문제와 해결 방법에 대해 알아보자.

비디오 플레이어 예제

이 예제는 video 요소와 같이 div를 보여주고 숨긴다.

(비디오는 외부참조를 할 때 source를 이용해서 사용하고 내부는 앵커로 참조하는 구나)

<button>Display video</button>

<div class="hidden">
  <video>
    <source src="https://raw.githubusercontent.com/mdn/learning-area/master/javascript/building-blocks/events/rabbit320.mp4" type="video/mp4">
    <source src="https://raw.githubusercontent.com/mdn/learning-area/master/javascript/building-blocks/events/rabbit320.webm" type="video/webm">
    <p>Your browser doesn't support HTML5 video. Here is a <a href="rabbit320.mp4">link to the video</a> instead.</p>
  </video>
</div>

버튼이 클릭 되었을 때,

div의 클래스 속성을 숨김에서 표시로 변경 함에 따라서 비디오가 표시 된다.

const btn = document.querySelector('button');
const **videoBox** = document.querySelector('**div**');

function displayVideo() {
  if (videoBox.**getAttribute**('class') === '**hidden**') {
    videoBox.**setAttribute**('class','**showing**');
  }
}

btn.addEventListener('click', **displayVideo**);

그런 다음 첫 번째는 div에, 두 번째는 video에 몇 가지 클릭 이벤트를 추가한다.

Display video

이제, 동영상 외부의 div영역을 클릭하면 상자(비디오박스)가 다시 숨겨져야 하고

동영상 자체를 클릭하게 되면 동영상이 재생되어 시작해야할 것이다.

However, 여기에 한 가지 문제가 있다!

확실하게, 현재 동영상을 클릭하면 재생이 시작되지만,

그것은 동시에 div가 숨겨지게 되는 일이 발생할 것이다.

This is because 해당 비디오가 div 안에 존재하기 때문이다. 즉, 비디오는 그 일부분임으로 비디오를 클릭하면 사실상 두 개의 이벤트가 모두 실행될 것이다.

💡 정리하자면, Bubbling은 내부 요소부터 부모 요소까지 이벤트를 발생 시킴. video는 div의 자식 요소. 따라서, video의 이벤트가 실행(재생)되고나서 바로 div의 이벤트(비디오박스 숨김)가 실행 됨.

이제 개념 정리는 끝났으니, 좀 더 깊은 이해를 위해 각 단계에서 진행되는 순서를 알아보자.

각 단계의 진행 순서


단계는 크게 Capture / Bubble / Target 세 가지로 분류 했고, 이는 이벤트가 발생했을 때 동시에 발생하는 것을 상기하도록 하자.

Capture 단계

  1. 브라우저는 최상위 요소인 HTML에 등록 된 Click Event Handler가 있는지 확인하고 있으면 실행
  2. HTML 내부의 다음 요소로 이동하여 동일한 작업을 수행
  3. 반복 실행하면서, 최종적으로 실제 클릭된 요소의 직접적인 부모 요소에 도달할 때까지 반복

Bubbling 단계

  1. 버블링 단계는 Capture단계와 정반대 행동을 함.
  2. 클릭 된 요소의 이벤트가 발생한 후
  3. 바로 위의 부모 요소가 등록된 이벤트를 갖고 있는지 확인 후 있으면 실행
  4. HTML요소에 도달할 때까지 반복

Target 단계에서

  1. 브라우저는 속성에 등록 된 클릭 이벤트에 대한 처리기가 있는지 확인 후 등록 된 경우 실행.
  2. 그런 다음 버블링이 ‘True’일 경우, 클릭한 요소의 직접적인 부모 요소로 이벤트를 전파한 뒤 HTML 요소에 도달할 때까지 이벤트 전파.
  3. 만약 버블링이 ‘False’인 경우 이벤트 전달은 되지 않음.
💡 임의적으로, Bubbling과 Capturing을 막기 위해 stopPropagation 또는 immdiatePropagation을 쓰는 경우가 있는데, 이것은 매우 좋지 못한 행위다. 사용하지 않도록 주의하자!

참고 자료

https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#event_bubbling_and_capture

profile
프론트엔드 개발자를 꿈꾸고 있습니다.

0개의 댓글