본문 바로가기
IT/HTML

이벤트 버블링과 캡처링

by Josh.P 2021. 5. 27.
반응형

버블링

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

3개의 요소가 form > div > p 형태로 중첩된 구조일 경우

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

가장 안 쪽의

를 클릭하면 순서대로 다음과 같은 일이 벌어진다.

  1. <p>에 할당된 onclick 핸들러가 동작한다.
  2. 바깥의 <div>에 할당된 핸들러가 동작한다.
  3. 그 바깥의 <form>에 할당된 핸들러가 동작한다.
  4. document 객체를 만날 때까지 각 요소에 할당된 onclick 핸들러가 동작한다.

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

event.target

부모 요소의 핸들러는 이벤트가 정확히 어디서 발생했는지 등에 대한 자세한 정보를 얻을 수 있다.

이벤트가 발생한 가장 안 쪽의 요소는 타겟(target) 요소라고 불리고, event.target을 사용해 접근할 수 있다.

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

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

버블링 중단하기

이벤트 버블링은 타겟 이벤트에서 시작해서 <html> 요소를 거쳐 document 객체를 만날 때까지 각 노드에서 모두 발생한다. 몇몇 이벤트는 window 객체까지 거슬러 올라가기도 한다. 이 때도 모든 핸들러가 호출된다.

핸들러에게 이벤트를 처리하고 난 후, 버블링을 중단하도록 명령할 수도 있다.

이벤트 객체의 메소드인 event.stopPropagation()을 사용하면 된다.

event.stopImmediatePropagation()

한 요소의 특정 이벤트를 처리하는 핸들러가 여러 개인 경우, 핸들러 중 하나가 버블링을 멈추더라도 나머지 핸들러는 여전히 동작한다.

버블링을 멈추고, 요소에 할당된 다른 핸들러의 동작도 막으려면 event.stopImmediatePropagation()을 사용해야 한다. 이 메소드를 사용하면 요소에 할당된 특정 이벤트를 처리하는 핸들러 모두가 동작하지 않는다.

반드시 필요한 경우에만 버블링을 막아야 한다.

아키텍처를 잘 고려해 반드시 막아야하는 상황에서만 버블링을 막아야 한다. event.stopPropagation()은 추후에 문제가 될 수 있는 상황을 만들어 낼 수 있다.

문제가 될 수 있는 시나리오

  • window 내에서 발생하는 클릭 이벤트를 전부 감지하기로 결정했을 때, document.addEventListener('click', ...)을 사용해야 하지만, stopPropagation이 설정되어 있으면 제대로 감지되지 않는다.

캡처링

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

  1. 캡처링 단계 - 이벤트가 하위 요소로 전파되는 단계
  2. 타겟 단계 - 이벤트가 실제 타겟 요소에 전달되는 단계
  3. 버블링 단계 - 이벤트가 상위 요소로 전파되는 단계
<html>
    <body>
        <table>
            <tbody>
                <tr>
                    <td></td>
                </tr>
            </tbody>
        </table>
    </body>
</html>

<td>를 클릭하면 이벤트가 최상위 조상(window)에서 시작해 아래로 전파(캡처링)되고, 이벤트가 타겟 요소에 도착해 실행된 후(타겟 단계), 다시 위로 전파(버블링)된다. 이런 과정을 통해 요소에 할당된 이벤트 핸들러가 호출된다.

on<event> 프로퍼티나 HTML 속성, addEventListener(event, handler)를 이용해 할당된 핸들러는 캡처링을 알 수 없다.

캡처링 단계에서 이벤트를 잡아내려면 addEventListenercapture 옵션을 true로 설정해야 한다.

elem.addEventListener(event, handler, {capture: true});
or
elem.addEventListener(event, handler, true);

위 예시 구조에서 캡처링과 버블링 이벤트를 할당하고, <td>를 클릭하면 다음과 같은 순서로 이벤트가 동작한다.

html → body → table → tbody → tr → td → td → tr → tbody → table → body → html

td에 캡처링과 버블링 두개를 등록했기 때문에 두 번 이벤트가 발생한다.

반응형

댓글