이벤트(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에 따르면, 이벤트의 흐름은
- 캡처링 단계(Capturing Phase)
- 타깃 단계(Target Phase)
- 버블링 단계(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
혹은options
의capture
속성을 이용하는 방법을 설명한다.
- 아래
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
메서드를 사용하면 된다.- 예를 들어,
transitioned
나onDOMContentLoaded
이는 프로퍼티를 통해서 이벤트 핸들러를 할당할 수 없다.
- 예를 들어,
-
이 방법은 핸들러를 단 하나만 할당할 수 있다는 단점이 있다.
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
인 경우 캡처링 단계에서 실행된다.
참고
- Event: https://developer.mozilla.org/en-US/docs/Web/API/Event
- Introduction to events: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events
- 브라우저 이벤트 소개: https://ko.javascript.info/introduction-browser-events
- 버블링과 캡처링: https://ko.javascript.info/bubbling-and-capturing
- https://dom.spec.whatwg.org/#introduction-to-dom-events
- Event: eventPhase property: https://developer.mozilla.org/en-US/docs/Web/API/Event/eventPhase
- Event reference: https://developer.mozilla.org/en-US/docs/Web/Events
- EventTarget: addEventListener() method: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener