본문 바로가기
Programming/JavaScript

버블링(bubbling), 그리고 target과 currentTarget - [Javascript _ 13]

by Muko 2020. 5. 29.

안녕하세요! muko 입니다.
오늘은 브라우저에서 이벤트와 관련된 개념인 버블링에 대해서 다뤄보도록 하겠습니다.

버블링 (bubbling)

브라우저에서 한 요소에 이벤트가 발생했을 때 이 요소에 할당된 이벤트 핸들러가 동작하고, 이어서 부모 요소의 이벤트 핸들러가 동작합니다. 가장 최상단의 요소를 만날 때 까지 이러한 과정이 반복되면서 각각의 요소에 할당된 핸들러가 동작합니다. 이러한 동작이 마치 거품이 일어나는 것과 비슷하다 해서 '버블링' 이라는 명칭이 붙게 되었습니다. 예시를 한 번 볼까요?

<div onclick="alert('div에 할당된 이벤트 핸들러!!!')">
  <span>span 영역을 클릭했는데도 DIV 영역에 할당된 핸들러가 동작합니다.</span>
</div>
span 영역을 클릭했는데도 DIV 영역에 할당된 핸들러가 동작합니다.

위의 예시를 동작해보면, 핸들러는 <div> 에 할당되어 있지만, <span> 같은 자식 요소, 혹은 중첩 태그를 클릭해도 동일하게 동작하는 것을 확인할 수 있습니다. 이렇게 동작하는 원인이 바로 버블링입니다.

더 이해하기 쉬운 예제를 살펴볼까요?

<style>
  div {
    margin: 10px;
    border: 1px solid blue;
  }
  div > div {
    border: 1px solid green;
  }
  div > div > div {
    border: 1px solid red;
  }
</style>

<div onclick="alert('outer')">OUTER
  <div onclick="alert('inner')">INNER
    <div onclick="alert('contents')">CONTENTS</div>
  </div>
</div>
OUTER
INNER
CONTENTS

가장 안쪽의 CONTENTS 를 클릭하면 순서대로 다음과 같은 동작이 일어납니다.

  1. <div>CONTENTS</div>에 할당된 onclick 핸들러가 동작합니다.
  2. CONTENTS 태그를 감싸고 있는 <div>INNER</div>에 할당된 핸들러가 동작합니다.
  3. INNER 태그를 감싸고 있는 <div>OUTER</div>에 할당된 핸들러가 동작합니다.
  4. document 객체를 만날 때 까지 각 요소에 할당된 onclick 핸들러가 동작합니다.

이렇게 동작하기 때문에 <div>CONTENTS</div>를 클릭하면 CONTENTS -> INNER -> OUTER 순서로 3개의 alert 창이 뜨는 것입니다. 이런 흐름을 '이벤트 버블링'이라고 부릅니다. 해당 이벤트가 일어난 가장 깊은 요소에서 시작해서 그 요소를 감싸고 있는 부모 요소들을 하나씩 타고 올라오면서 발생하기 때문입니다. 마치 물속에서 거품이 올라오는 것과 비슷하죠?

# 거의 모든 이벤트는 버블링이 됩니다.
다만 focus 이벤트와 같이 버블링이 되지 않는 이벤트가 조금 있습니다. 그 외에 대부분의 이벤트는 버블링 동작이 일어납니다.

버블링 중단하기

이벤트 버블링은 target 이벤트에서 시작해서 <html> 요소를 거쳐 document 객체를 만날 때까지 각 노드에서 모두 발생합니다. 어떤 이벤트는 window 객체까지 올라가기도 합니다. 이 때도 위와 동일하게 모든 이벤트 핸들러가 호출됩니다.

그런데 이벤트 핸들러에게 이벤트 처리 동작이 끝나고 나서 event.stopPropagation()를 사용해서 버블링이 진행되지 않도록 명령하는 것이 가능합니다.

<div onclick="alert('버블링이 발생하지 않습니다')">
  <button onclick="event.stopPropagation()">클릭</button>
</div>

위의 예시에서 <button>을 클릭해도 body.onclick은 동작하지 않는 것을 확인할 수 있습니다.

# event.stopImmediatePropagation()
한 요소의 특정 이벤트를 처리하는 이벤트 핸들러가 여러개가 있을 경우, 핸들러 중 하나가 버블링을 멈추더라도 나머지 핸들러는 여전히 동작하는 것을 확인할 수 있습니다.
event.stopPropagation()은 현재 요소 위쪽으로 진행되는 버블링은 막아주지만, 다른 핸들러들이 동작하는 것을 막지는 못합니다. 그래서 버블링도 멈추고 요소에 할당된 다른 이벤트 핸들러의 동작도 막기 위해서는 event.stopImmediatePropagation()을 사용해야 합니다. 이 메서드를 사용하면 요소에 할당된 이벤트 핸들러들이 동작하지 않게 됩니다.

event.target과 event.currentTarget

이벤트가 발생하게 되면 해당 이벤트가 어디서 발생했는지, 어떤 이벤트가 발생했는지 등의 자세한 정보를 얻을 수 있습니다. 이 때 이벤트가 발생한 가장 안쪽의 요소를 타겟(target) 요소라고 하고, event.target 을 사용해서 접근할 수 있습니다.

event.targetthis(=event.currentTarget)는 다음과 같은 차이점이 있습니다.

  • event.target은 실제 이벤트가 시작된 '타겟'요소 입니다. 버블링이 진행되어도 변하지 않습니다.
  • this는 '현재'요소로 현재 실행 중인 핸들러가 할당된 요소를 참조합니다.

예시로 위의 두 가지가 어떤 차이를 가지는지 살펴보도록 하죠.

<style>
  div {
    margin: 10px;
    border: 1px solid blue;
  }
  div > div {
    border: 1px solid green;
  }
  div > div > div {
    border: 1px solid red;
  }
</style>

<script>
  const handleClick = (event) => {
    event.target.style.backgroundColor = 'yellow';

    setTimeout(() => {
      alert('target: ' + event.target.textContent + ', this:' + this.textContent);
      event.target.style.backgroundColor = '';
    }, 0);
  };
</script>

<div onclick="handleClick">OUTER
  <div onclick="handleClick">INNER
    <div onclick="handleClick">CONTENTS</div>
  </div>
</div>
OUTER
INNER
CONTENTS

OUTER를 클릭했을 때는 event.targetthis가 동일한 것을 확인할 수 있습니다.

이제 이벤트를 다룰 수 있게 되었네요!! 👏👏👏
다음 시간에는 세 번째 토이프로젝트인 '계산기 만들기'를 진행해보도록 하겠습니다 :)

다음 포스팅

'계산기 만들기' 토이 프로젝트(1) - [Javascript 입문 _14]

댓글0