이벤트(Event) 흐름

이벤트 캡처링과 버블링까지


Table Of Contents


TL;DR


  • 웹에서 발생하는 이벤트는 캡처링, 타깃, 버블링 단계를 거칠 수 있다.
  • 캡처링 단계에서는 이벤트가 상위 요소에서 하위 요소로, 버블링 단계에서는 이벤트가 하위 요소에서 상위 요소로 전파된다.
  • 어떤 이벤트는 버블링 단계를 거치지 않을 수도 있고, 인위적으로 버블링 단계를 멈출 수도 있다.
  • 이벤트 핸들러가 캡처링 단계에서 실행될 지, 버블링 단계에서 실행될 지는 addEventListener의 옵션에서 결정할 수 있다.

(브라우저) 이벤트(Event)


  • 이벤트란 시스템에서 일어나는 일들이다.
    • 시스템은 이벤트가 발생하면 일종의 신호를 생성(produce / fire)하고, 자동으로 조치를 취할 수 있는 메커니즘을 제공한다.
    • 이벤트는 브라우저 창 내부에서 실행되며, 브라우저의 특정 항목에 연결되려는 경향이 있다.
      • 단일 element, element의 집합, 현재 탭에 로드된 HTML 문서 또는 전체 브라우저 창 등에 연결된다.
  • 웹 이벤트란 JS에서 정의되는 것이 아니라, 브라우저에 의해서 정의된다. JS는 이벤트에 listener를 추가해 특정 이벤트가 발생할 때마다 원하는 코드가 실행되도록 해준다.

예시

  • 마우스 이벤트
  • click: element 위에서 마우스 왼쪽 버튼을 눌렀을 때(터치했을 때)
  • contextmenu: element 위에서 마우스 오른쪽 버튼을 눌렀을 때
  • 폼 요소 이벤트
    • submit: <form>을 제출할 때
    • focus: <input>같은 요소에 focus할 때
  • 키보드 이벤트
    • keydown: 키보드 버튼을 누를 때
    • keyup: 키보드 버튼을 뗄때
  • 브라우저 창 이벤트
    • resize: 브라우저 창의 크기가 조절될 때
    • beforeunload: 브라우저 창을 닫으려 할 때
  • 문서 및 로딩 이벤트
    • load: 웹 페이지의 로딩이 끝났을 때
    • DOMContentLoaded: HTML 문서가 완전히 로드되고 파싱이 끝났을 때
  • 미디어 이벤트
    • play: 영상이 재생될 때
    • pause: 영상이 일시정지될 때
    • ended: 영상이 종료되었을 때
  • 에러 이벤트
    • error: 네트워크 요청, 미디어, 스크립트 실행 등에서 에러가 발생했을 때

이벤트 흐름


  • DOM Standard에 따르면, 이벤트의 흐름은
    1. 캡처링 단계(Capturing Phase)
    2. 타깃 단계(Target Phase)
    3. 버블링 단계(Bubbling Phase)의 3단계로 이루어진다.
  • Event 객체에는 eventPhase 속성이 있어 현재 평가 중인 이벤트 흐름의 단계를 알 수 있다.
    • Event.NONE (0)
      • 이벤트가 아직 발생하지 않은 상태
    • Event.CAPTURING_PHASE (1)
    • Event.AT_TARGET (2)
    • Event.BUBBLING_PHASE (3)

1. 캡처링 단계(Capturing Phase)

  • 캡처링 단계는 이벤트가 상위 요소에서 하위 요소로 전달되는 과정이다.
    • Window에서부터 시작해서 Document를 거쳐 이벤트가 대상의 부모에 도달할 때까지 캡처링 단계가 지속된다.
  • EventTarget.addEventListener()로 등록한 이벤트 리스너 중 캡처 모드로 등록한 리스너는 이 단계에서 실행된다.
    • 아래 addEventListener 메서드에서 useCapture 혹은 optionscapture 속성을 이용하는 방법을 설명한다.

2. 타깃 단계(Target Phase)

  • 이벤트가 이벤트 대상(타깃)에 도착한 단계이다.
  • Event.bubbles 속성이 false면, 이 단계에서 이벤트 처리를 마친다.

3. 버블링 단계(Bubbling Phase).

  • 버블링 단계는 이벤트가 하위 요소에서 상위 요소로 전달되는 과정이다.
    • 이벤트 대상의 부모에서 시작해 다시 Docuemnt를 거쳐 Window에 도달할 때까지 지속된다.
  • Event.bubbles 속성이 true여야 발생한다.
    • 즉, 모든 이벤트가 버블링 단계를 거치는 것은 아니다. focus같이 버블링되지 않는 이벤트도 있다.
  • 이벤트 리스너 중 캡처 모드가 아닌 리스너는 이 단계에서 실행된다.

버블링 중단하기: event.stopPropagation()

  • 핸들러 내부에서 event.stopPropagation()을 호출해 이벤트가 더이상 버블링되지 않도록 설정할 수 있다.

이벤트 핸들러(Event Handler)


이벤트가 발생했을 때, 이벤트에 반응하려면 핸들러를 할당해야 한다.

이벤트 핸들러 사용하기

1. (지양) 인라인 이벤트 핸들러(Inline event handler)

  • HTML 안의 on<event> 속성을 통해 핸들러를 할당할 수 있다.
    <div onclick="alert('HI!')">click me!</div>
    혹은
    <!-- HTML --> <div onclick="alertHi()">click me!</div>
    // JS function alertHI() { alert("HI!"); }
    같은 방법으로 이벤트 핸들러를 등록할 수 있다.
  • 하지만 인라인 이벤트 핸들러를 사용하면 HTML과 JS가 섞이기 때문에, 코드를 분석하기 어려워진다. 따라서 로직은 JS로 분리하는 것이 좋다고 한다.

2. DOM의 이벤트 핸들러 프로퍼티

  • DOM의 on<event> 속성을 통해서도 핸들러를 할당할 수 있다.

    const divElement = document.querySelector("div"); divElement.onclick = function alertHi() { alert("HI!"); };
  • 만약 핸들러를 제거하고 싶다면, element.onclick = null같이 null을 할당하면 된다.

  • 또한, 어떤 이벤트는 DOM 프로퍼티로 할당할 수 없다. 이럴 때는 아래에서 소개하는 addEventListener 메서드를 사용하면 된다.

    • 예를 들어, transitionedonDOMContentLoaded 이는 프로퍼티를 통해서 이벤트 핸들러를 할당할 수 없다.
  • 이 방법은 핸들러를 단 하나만 할당할 수 있다는 단점이 있다.

3. EventTarget.addEventListener() 메서드

  • EventTarget 인터페이스의 addEventListner() 메서드를 호출해 이벤트 핸들러를 등록하는 방법이 있고, 가장 추천되는 방법인 것 같다.

  • 이벤트 핸들러를 등록하려면 아래처럼 사용하면 된다.

    const divElement = document.querySelector("div"); function alertHi() { alert("HI!"); } divElement.addEventListener("click", alertHi);
  • addEventListener를 사용하면 element에 여러 핸들러를 등록하는 것도 가능하다.

    • 이 핸들러들은 설정한 순서대로 동작한다.
divElement.addEventListener("click", (e) => console.log(1)); divElement.addEventListener("click", (e) => console.log(2)); // 1 // 2 // 순서대로 출력된다.

명세

EventTarget.addEventListener(type, listener); EventTarget.addEventListener(type, listener, options); EventTarget.addEventListener(type, listener, useCapture);

EventTarget이 지정한 type 유형의 이벤트를 수신할 때마다 호출할 함수listener를 지정한다.

  • EventTarget
    • 이벤트의 대상이다.
    • Element, Document, Window등이 있다.
  • type
    • 수신할 이벤트의 유형이다.
    • 대소문자를 구분한다.
    • 이벤트의 종류는 mdn-Event reference에서 확인할 수 있다.
  • listener
    • 지정할 이벤트를 수신할 객체이다.
  • options
    • 이벤트 핸들러의 특성을 지정할 수 있는 객체이다.
    • 옵션의 종류는 mdn docs를 참고하자.
    • 대표적으로 capture 옵션은 listener 함수의 실행 시점을 결정한다. 기본값은 capture=false로, listener 함수는 버블링 단계에서 실행된다. capture=true인 경우, listener 함수는 캡처링 단계에서만 실행된다.
  • useCapture
    • useCapture=false인 경우, listner 함수는 버블링 단계에서 실행되고, useCapture=true인 경우 캡처링 단계에서 실행된다.

참고