DOM 스펙은 W3C에서 Level 단위로 만들어지고 있다.
DOM 레벨 1은 HTML, XML 문서 구조를 정의하는데 초점이 맞춰져 있었다. 이후 발표된 DOM 레벨 2, 3은 위 구조에 따른 상호작용 기능 추가 및 고급 XML 기능을 지원하는데 집중했다.
DOM 레벨 3 이벤트 문서는 원래 2000년과 2003년 사이에 개발되었으며 구현자의 추가 피드백과 관심이 있을 때까지 W3C 메모로 게시되었다. 2006년에 Recommendation Track에서 수정 및 진행을 위해 선택되었고, 현재 구현 상태와 스크립트 작성자의 요구를 반영하도록 수정되었다.
오늘은 이벤트에 대해 알아보자.
이벤트는 다음 상황에서 발생한다.
- user interaction
- DOM change
- created or modified by a script
- dispatched via EventTarget.dispatchEvent()
Event.eventPhase
Event의 읽기 전용 속성인 eventPhase는 현재 평가중인 이벤트 흐름의 단계를 나타낸다.
const phase = event.eventPhase;
- Event.NONE: 현재 처리중인 이벤트가 없는 상태 (값: 0)
- Event.CAPTURING_PHASE: 캡쳐링 단계, window, document, HTMLHtmlElement 순서대로 target의 부모에 도달할 때까지 전파된다. (값: 1)
- Event.AT_TARGET: 이벤트가 이벤트 대상에 도착한 상태, 이 단계에 등록된 이벤트 리스너가 호출된다. (값: 2)
- Event.BUBBLING_PHASE: 버블링 단계, 캡쳐링의 역순으로 전파된다. (값: 3)
각 단계가 이벤트에 상수로 정의되어있어서 e.eventPhase === e.AT_TARGET 처럼 비교할 수 있다.
console.log(e.eventPhase, e.NONE, e.CAPTURING_PHASE, e.AT_TARGET, e.BUBBLING_PHASE)
Event Flow
이벤트 객체는 DOM event flow에 의해 결정된 대로 DOM tree를 통해 전파된다. 또한, 우리는 dispatchEvent() 메서드를 사용해서 이벤트 객체를 전달할 수 있다. 디스패치 전에 stopPropagation() 가 호출되면 모든 단계를 건너뛴다.
Synchronous and asynchronous events
이벤트는 동기/비동기로 전달될 수 있다.
동기 이벤트는 사용자 상호작용 및 DOM 변경, 다른 이벤트 측면에 대해, 발생한 시간 순으로 가상 큐에 대해 선입선출(FIFO)과 같은 동작을 한다.
비동기 이벤트는 작업 결과가 완료될 때 전달된다.
예를 들어, document를 불러올 때, 인라인 스크립트 요소를 분석하고 실행한다. load 이벤트는 스크립트 요소로 큐에 들어가서 비동기적으로 실행된다. 비동기 이벤트이기 때문에 문서 로드 중에 발생하는 다른 동기 이벤트(e.g. DOMContentLoaded event)의 순서와 관련된 보장되지 않는다.
Trusted Events
사용자와의 상호작용 혹은 DOM 변경으로 발생한 이벤트를 Trusted Event라 한다. 그 반대의 경우로 스크립트에 의해 생성/변경되거나 createEvent(), initEvent(), EventTarget.dispatchEvent() 로 발생했을 때의 경우가 있다. (createEvent(), initEvent()는 deprecated 됐다.)
Event.isTrusted 메소드(읽기 전용)는 Trusted Event를 판별해준다. Trusted Event 일 때, true이다.
Untrusted Event에 대해서 click 이벤트를 제외하고는 preventDefault() 가 호출되어서 default action은 동작하지 않는다. click 이벤트가 제외된 이유는 이전 버전과의 상호 호환성 때문이다.
Activation triggers and behavior
특정 event targets (e.g. 링크나 버튼 element)는 activation trigger (e.g. 링크 클릭, focus를 가지고 있지않아도 해당한다.)에 따른 activation behavior (e.g. 링크 이동)를 가지고 있다.
Constructing Mouse and Keyboard Events
일반적으로 Event 인터페이스 또는 Event 인터페이스에서 상속된 인터페이스의 생성자가 호출되면 [DOM]에 설명된 단계를 따라야 한다. 하지만, KeyboardEvent 및 MouseEvent 인터페이스는 Event 객체의 키 수정자의 내부 상태, 특히 getModifierState() 및 getModifierState() 메서드를 사용하여 쿼리 된 내부 상태를 초기화하기 위한 추가적인 dictionary member를 제공한다. 이 부분은 Event 객체를 초기화하기 위한 DOM4 단계를 보완한다.
[Exposed=(Window,Worker,AudioWorklet)]
interface Event {
constructor(DOMString type, optional EventInit eventInitDict = {});
readonly attribute DOMString type;
readonly attribute EventTarget? target;
readonly attribute EventTarget? srcElement; // legacy
readonly attribute EventTarget? currentTarget;
sequence<EventTarget> composedPath();
const unsigned short NONE = 0;
const unsigned short CAPTURING_PHASE = 1;
const unsigned short AT_TARGET = 2;
const unsigned short BUBBLING_PHASE = 3;
readonly attribute unsigned short eventPhase;
undefined stopPropagation();
attribute boolean cancelBubble; // legacy alias of .stopPropagation()
undefined stopImmediatePropagation();
readonly attribute boolean bubbles;
readonly attribute boolean cancelable;
attribute boolean returnValue; // legacy
undefined preventDefault();
readonly attribute boolean defaultPrevented;
readonly attribute boolean composed;
[LegacyUnforgeable] readonly attribute boolean isTrusted;
readonly attribute DOMHighResTimeStamp timeStamp;
undefined initEvent(DOMString type, optional boolean bubbles = false, optional boolean cancelable = false); // legacy
};
dictionary EventInit {
boolean bubbles = false;
boolean cancelable = false;
boolean composed = false;
};
Event Types
User Interface Events
사용자 인터페이스 이벤트 모듈에는 사용자 인터페이스 및 문서 조작과 관련된 기본 이벤트 유형이 포함되어 있다.
Interface UIEvent
DOM Level 2에서 도입되었다.
[Constructor(DOMString type, optional UIEventInit eventInitDict)]
interface UIEvent : Event {
readonly attribute Window? view;
readonly attribute long detail;
};
UIEvent 인터페이스는 사용자 인터페이스 이벤트와 관련된 고유의 contextual information를 제공한다.
- view: Window가 누가 생성했는지 지정한다.
- detail: Event이벤트 유형에 따라 에 대한 일부 세부 정보를 지정한다.
dictionary UIEventInit : EventInit {
Window? view = null;
long detail = 0;
};
UI Event Types
user interface로 부터 생성된 이벤트는 UIEvent interface를 사용한다. 그 이외의 경우는 모두 Event interface를 사용한다.
- load
- unload
- abort
- error
- select
Focus Events
Interface FocusEvent
이 명세에서 도입되었다.
FocusEvent 인터페이스는 포커스 이벤트에 관련하는 고유의 contextual information(문맥 정보)를 제공한다.
FocusEventInit를 옵션으로 FocusEvent 인터페이스의 인스턴스를 만들 수 있다.
[Constructor(DOMString type, optional FocusEventInit eventInitDict)]
interface FocusEvent : UIEvent {
readonly attribute EventTarget? relatedTarget;
};
중첩된 브라우징 컨텍스트의 보안상의 이유로 중첩된 콘텍스트로 탭을 이동하거나 중첩 콘텍스트에서 빠져나올 때, EventTarget 은 null이어야한다.
초기화되지 않은 EventTarget은 반드시 null 이여야 한다.
dictionary FocusEventInit : UIEventInit {
EventTarget? relatedTarget = null;
};
Focus Event Order
이 사양에 정의된 포커스 이벤트는 서로에 대해 설정된 순서로 발생한다.
즉, 아래 예제에서 focus 이벤트 발생 순서는 다음과 같다.
<form id="form">
<input type="text" placeholder="text input">
<input type="password" placeholder="password">
</form>
<script>
const form = document.getElementById('form');
form.addEventListener('focusin', (event) => {
event.target.style.background = 'pink';
});
form.addEventListener('focusout', (event) => {
event.target.style.background = '';
});
</script>
- text input을 클릭했을 때
- text input에 대해 focusin 발생으로 background가 pink로 변경
- text input에 대해 focus 발생으로 테두리 파란색으로 변경
- password를 클릭했을 때
- text input에 대해 focusout 발생으로 background가 제거
- password에 대해 focusin 발생으로 background가 pink로 변경
- text input에 대해 blur 발생으로 테두리 파랑색 제거
- password에 대해 focus 발생으로 테두리 파랑색 변경
Document Focus and Focus Context
Document는 항상 focus element(document 자기 자신도 됨)와 지속적인 focus ring 을 가지고 있다.
Trusted event일 때, UIEvent. detail 은 0이다. (아무 의미 없음)
Mouse Events
마우스 이벤트 모듈은 마우스나 트랙볼과 같은 포인팅 입력 장치와 함께 사용하도록 특별히 설계되었다.
Interface MouseEvent
DOM Level 2에서 도입되었고, 이 명세에서 수정되었다.
MouseEvent 인터페이스는 마우스 이벤트에 관련하는 고유의 문맥 정보를 제공한다.
중첩된 요소의 경우, 마우스 이벤트는 항상 가장 깊이 중첩된 요소를 대상으로 한다.
[Constructor(DOMString type, optional MouseEventInit eventInitDict)]
interface MouseEvent : UIEvent {
readonly attribute long screenX;
readonly attribute long screenY;
readonly attribute long clientX;
readonly attribute long clientY;
readonly attribute boolean ctrlKey;
readonly attribute boolean shiftKey;
readonly attribute boolean altKey;
readonly attribute boolean metaKey;
readonly attribute short button;
readonly attribute unsigned short buttons;
readonly attribute EventTarget? relatedTarget;
boolean getModifierState(DOMString keyArg);
};
- MouseEvent.screenX
- MouseEvent.screenY
- MouseEvent.clientX
- MouseEvent.clientY
- MouseEvent.ctrlKey
- un-initialized value: false
- MouseEvent.shiftKey
- un-initialized value: false
- MouseEvent.altKey
- un-initialized value: false
- MouseEvent.metaKey
- un-initialized value: false
- MouseEvent.button
- 마우스 버튼을 누르거나 놓으면, 마우스 이벤트가 발생하는 동안 button 이 그 상태를 표현한다.
- 0: 왼쪽 버튼 또는 un-initialized value
- 1: 보조 버튼(일반적으로 마우스 휠과 결합되는 중간 버튼)
- 2: 보조 버튼(일반적으로 콘텍스트 메뉴를 표시하는 데 자주 사용되는 오른쪽 버튼)
- 일부 포인팅 장치는 더 많은 버튼 상태를 제공하거나 시뮬레이션하며, 이러한 버튼을 나타내는 데 2보다 높 거나 낮은 값을 0사용할 수 있다.
- MouseEvent.buttons
- 어떤 버튼들이 동시에 눌렸는지 알 수 있다.
- 예: 값 3은 현재 왼쪽과 오른쪽 버튼이 모두 눌림, 5는 현재 왼쪽과 가운데 버튼이 모두 눌림
- 0: 현재 활성화된 버튼이 없음
- 1: 장치의 기본 버튼
- 2: 보조 버튼(일반적으로 콘텍스트 메뉴를 표시하는 데 자주 사용되는 오른쪽 버튼)
- 4: 보조 버튼(일반적으로 마우스 휠과 결합되는 중간 버튼)
- 어떤 버튼들이 동시에 눌렸는지 알 수 있다.
- MouseEvent.relatedTarget
- un-initialized value: null
- MouseEvent.getModifierState(keyArg)
- §5.3.2 Modifier keys의 현재 상태를 반환
- modifier key이고 modifier가 활성화되어있으면 true 반환, 이외에는 false 반환
dictionary MouseEventInit : EventModifierInit {
long screenX = 0;
long screenY = 0;
long clientX = 0;
long clientY = 0;
short button = 0;
unsigned short buttons = 0;
EventTarget? relatedTarget = null;
};
Event Modifier Initializers
dictionary EventModifierInit : UIEventInit {
boolean ctrlKey = false;
boolean shiftKey = false;
boolean altKey = false;
boolean metaKey = false;
boolean modifierAltGraph = false;
boolean modifierCapsLock = false;
boolean modifierFn = false;
boolean modifierFnLock = false;
boolean modifierHyper = false;
boolean modifierNumLock = false;
boolean modifierScrollLock = false;
boolean modifierSuper = false;
boolean modifierSymbol = false;
boolean modifierSymbolLock = false;
};
Mouse Event Order
이 사양에 정의된 특정 마우스 이벤트는 서로에 대해 설정된 순서로 발생한다.
아래는 포인팅 장치의 커서가 element 위로 이동할 때 반드시 발생해야 하는 이벤트 시퀀스이다.
아래는 포인팅 장치를 요소 A로 이동한 다음 중첩 요소 B로 이동한 다음 다시 뒤로 이동할 때 발생해야 하는 이벤트 시퀀스이다.
즉, 겹쳤을 때 mouseover, mouseout은 B를 포함하지 않은 A영역과 B영역으로 발생한다고 보면 되고, mouseenter와 mouseleave는 A와 B의 경계선이 기준임을 알 수 있다.
때로는 CSS를 사용하여 요소가 시각적으로 겹칠 수 있다. 다음 예에서 A, B, C로 레이블이 지정된 세 요소는 모두 웹 페이지에서 동일한 차원과 절대 위치를 가진다. DOM에서 요소 C는 B의 자식이고 B는 A의 자식이다.
포인팅 장치가 요소 스택 외부에서 C 레이블이 지정된 요소로 이동한 다음 다시 밖으로 이동할 때 다음 일련의 이벤트가 발생한다.
아래는 포인팅 장치(예: 마우스 버튼 또는 트랙패드)와 연결된 버튼을 요소 위에서 눌렀다가 놓을 때, 발생하는 일반적인 이벤트 시퀀스이다.
event target 가 마우스 이벤트 발생 중에 DOM에서 제거된다면, 남은 이벤트 순서들은 발생하지 않아야 한다.
Mouse Event Types
중첩 요소의 경우 마우스 이벤트 유형은 항상 가장 깊이 중첩된 요소를 대상으로 한다. 대상 요소의 조상은 하위 요소 내에서 발생하는 마우스 이벤트의 알림을 얻기 위해 버블링 될 수 있다.
- click
- dblclick
- mousedown
- mouseenter
- Trusted event일 때, UIEvent. detail 은 0, 의미 없음.
- Trusted event일 때, MouseEvent.relatedTarget : 포인팅 장치가 종료되는 event target.
- mouseleave
- Trusted event일 때, MouseEvent.relatedTarget : 포인트 장치가 진입하는 event target
- mousemove
- mouseout
- mouseover
- mouseup
Wheel Events
휠은 하나 이상의 공간 차원에서 회전할 수 있고 포인터 장치와 연관될 수 있는 장치이다. 좌표계는 환경 구성에 따라 다르다.
Interface WheelEvent
[Constructor(DOMString type, optional WheelEventInit eventInitDict)]
interface WheelEvent : MouseEvent {
// DeltaModeCode
const unsigned long DOM_DELTA_PIXEL = 0x00;
const unsigned long DOM_DELTA_LINE = 0x01;
const unsigned long DOM_DELTA_PAGE = 0x02;
readonly attribute double deltaX;
readonly attribute double deltaY;
readonly attribute double deltaZ;
readonly attribute unsigned long deltaMode;
};
dictionary WheelEventInit : MouseEventInit {
double deltaX = 0.0;
double deltaY = 0.0;
double deltaZ = 0.0;
unsigned long deltaMode = 0;
};
Wheel Event Types
- wheel
Input Events
DOM이 업데이트될 때마다 입력 이벤트가 알림으로 전송된다.
Interface InputEvent
DOM 레벨 3에 도입되었다.
[Constructor(DOMString type, optional InputEventInit eventInitDict)]
interface InputEvent : UIEvent {
readonly attribute DOMString data;
readonly attribute boolean isComposing;
};
- InputEvent.data
- data 입력 방법에 의해 생성된 문자의 값을 보유한다.
- Character는 [UAX15]에 정의되어있는 Unicode normalization form NFC에 의해 정규화되어야 한다.
- un-initialized value: "" (빈 문자열)
- InputEvent.isComposing
- composition 분기에서 이벤트가 발생하면, true
- compositionstart 이벤트 후, compositionend 이벤트 전
- un-initialized value: false
- composition 분기에서 이벤트가 발생하면, true
dictionary InputEventInit : UIEventInit {
DOMString data = "";
boolean isComposing = false;
};
composition event가 궁금하면 우선 다음 링크에서 아래 예제를 실행해보자. (아래에도 설명이 있다.)
Input Event Order
이 사양에 정의된 입력 이벤트는 서로에 대해 설정된 순서로 발생해야 한다.
Input Event Types
input, select, textarea의 값이 수정되려고 할 때 발생. contenteditable, designMode에도 적용된다.
- beforeinput
- user agent는 DOM을 업데이트하려고 할 때 이 이벤트를 전달해야 한다.
- input
- user agent는 DOM가 업데이트된 후 즉시 이 이벤트를 전달해야 한다.
Keyboard Events
키보드 이벤트는 장치에 따라 다르다. 입력 장치의 기능과 운영 체제에서 매핑되는 방법에 따라 다르다.
키보드 이벤트는 텍스트 입력을 제공하는 한 가지 양식일 뿐이다.
Interface KeyboardEvent
이 사양에서 도입되었다.
[Constructor(DOMString type, optional KeyboardEventInit eventInitDict)]
interface KeyboardEvent : UIEvent {
// KeyLocationCode
const unsigned long DOM_KEY_LOCATION_STANDARD = 0x00;
const unsigned long DOM_KEY_LOCATION_LEFT = 0x01;
const unsigned long DOM_KEY_LOCATION_RIGHT = 0x02;
const unsigned long DOM_KEY_LOCATION_NUMPAD = 0x03;
readonly attribute DOMString key;
readonly attribute DOMString code;
readonly attribute unsigned long location;
readonly attribute boolean ctrlKey;
readonly attribute boolean shiftKey;
readonly attribute boolean altKey;
readonly attribute boolean metaKey;
readonly attribute boolean repeat;
readonly attribute boolean isComposing;
boolean getModifierState(DOMString keyArg);
};
dictionary KeyboardEventInit : EventModifierInit {
DOMString key = "";
DOMString code = "";
unsigned long location = 0;
boolean repeat = false;
boolean isComposing = false;
};
Keyboard Event Key Location
표에 없는 다른 키들에 대해, location 속성의 값은 항상 DOM_KEY_LOCATION_STANDARD 이다.
Keyboard Event Order
이 사양에 정의된 키보드 이벤트는 지정된 키에 대해 아래와 같이 설정된 순서대로 발생한다.
키를 계속 누르고 있으면 환경에 따라 다음 이벤트가 반복될 수 있다.
Keyboard Event Types
- keydown
- keyup
Composition Events
합성 이벤트는 키보드에서 일반적으로 사용할 수 없는 문자의 사용을 허용하기 위해 키보드 이벤트보다 보완적 또는 대체 방식으로 텍스트를 입력하는 수단을 제공한다. 예를 들어, 합성 이벤트는 표준 미국 키보드가 없음에도 불구하고 문자에 악센트를 추가하고, 기본 구성 요소 또는 범주에서 많은 아시아 언어의 logogram(로고 그램)을 작성하고, 모바일 장치의 키 누름 조합에서 단어 선택을 선택하는 데 사용될 수 있다. 또한, 키보드를 사용하거나 음성 인식 프로세서를 사용하여 음성 명령을 텍스트로 변환한다.
Interface CompositionEvent
이 사양에서 도입되었다.
CompositionEvent 인터페이스는 합성 이벤트에 관련하는 고유의 문맥 정보를 제공한다.
[Constructor(DOMString type, optional CompositionEventInit eventInitDict)]
interface CompositionEvent : UIEvent {
readonly attribute DOMString data;
};
dictionary CompositionEventInit : UIEventInit {
DOMString data = "";
};
Composition Event Order
이 사양에 정의된 합성 이벤트는 서로에 대해 다음 설정 순서로 발생해야 한다.
Handwriting Recognition Systems
Canceling Composition Events
초기 compositionstart 이벤트가 취소되면 텍스트 Composition 세션을 종료해야 한다. Composition 세션이 종료되었는지 여부에 관계없이 compositionend 이벤트는 반드시 전송되어야 한다.
Key Events During Composition
Input Events During Composition
Composition 세션 동안에는 beforeinput 이후 input 이벤트가 전송되기 전에 compositionupdate가 전달되어야 한다.
Composition Event Types
- compositionstart
- compositionupdate
- compositionend
Key Codes
key code는 키보드 이벤트와 관련된 물리적 키를 식별하는 데 사용할 수 있는 키보드 이벤트의 속성이다. code 속성의 주요 목적은 물리적 위치를 기반으로 키를 식별하는 일관된 방법을 제공하는 것이다.
The Relationship Between key and code
- key
- code
- code 속성이 적용된 레이아웃 수정 없이, 사용자가 누른 키에 관심이 있는 사용자를 위한 것이다.
- 예: 원격 데스크톱 클라이언트에서 모든 키를 원격 호스트로 보내기.
Legacy KeyboardEvent supplemental interface
키보드에 대한 브라우저 지원은 전통적으로 세 가지 임시 속성 keyCode, charCode, which에 의존했다.
이 세 가지 속성은 모두 누른 키의 일부를 나타내는 숫자 코드를 반환한다.
keyCode는 키 자체의 인덱스이다.
charCode는 문자 키의 ASCII 값입니다.
which는 사용 가능한 경우 문자 값이고, 그렇지 않은 경우 키 인덱스이다.
이러한 속성의 값과 속성의 가용성은 플랫폼, 키보드 언어 및 레이아웃, 사용자 에이전트, 버전 및 이벤트 유형에 걸쳐 일관되지 않는다.
partial interface KeyboardEvent {
// The following support legacy user agents
readonly attribute unsigned long charCode;
readonly attribute unsigned long keyCode;
readonly attribute unsigned long which;
};
partial dictionary KeyboardEventInit {
// The following support legacy user agents
unsigned long charCode = 0;
unsigned long keyCode = 0;
unsigned long which = 0;
};
Extending Events
var chartData = ...;
var evt = document.createEvent("CustomEvent");
evt.initCustomEvent( "updateChart", true, false, { data: chartData });
document.documentElement.dispatchEvent(evt);
Changes
Changes between DOM Level 2 Events and UI Events
- focus, blur 이벤트가 추가되었다.
- UIEvent 모듈, MouseEvent 모듈, dblclick 이벤트가 추가되었다.
- 새로운 사양은 DOM 이벤트 흐름, 이벤트 유형 및 DOM 인터페이스 간의 더 나은 분리를 제공한다.
- 이제 이벤트 리스너가 정렬된다. DOM 레벨 2에서는 이벤트 순서가 지정되지 않았다.
참고
https://developer.mozilla.org/en-US/docs/Web/API/Event/eventPhase
https://www.w3.org/TR/DOM-Level-3-Events/#event-flow
'Tech > DOM' 카테고리의 다른 글
가로 스크롤 앨범을 만들어보자. (0) | 2021.08.22 |
---|---|
HTMLElement (0) | 2021.08.02 |
DOMString (0) | 2021.07.31 |
This value was evaluated upon first expanding. it may have changed since then. (0) | 2021.07.31 |
classList (0) | 2021.07.31 |
댓글