Copyright © 2025 World Wide Web Consortium. W3C® liability, trademark and permissive document license rules apply.
이 명세의 기능들은 마우스, 펜, 터치스크린 등 장치로부터 하드웨어에 구애받지 않는 포인터 입력을 처리하기 위한 이벤트와 관련 인터페이스를 설명하는 W3C 권고안인 Pointer Events에 정의된 내용을 확장하거나 수정합니다. 기존 마우스 기반 콘텐츠와의 호환성을 위해, 이 명세는 다른 포인터 장치 타입에 대해 Mouse Events를 발생시키는 매핑도 설명합니다.
이 섹션은 해당 문서가 발행된 시점의 상태를 설명합니다. 현재 W3C 발행물 목록 및 이 기술 보고서의 최신 개정은 W3C 표준 및 초안 색인에서 찾을 수 있습니다.
이 명세는 [PointerEvents3]의 업데이트입니다. 더 많은 사용 사례를 지원하는 편집상 명확화와 새로운 기능을 포함합니다.
이 문서는 Pointer Events Working Group이 권고안 절차(Recommendation track)를 사용하여 Working Draft로 발행한 것입니다.
Working Draft로의 발행은 W3C 및 그 회원의 보증을 의미하지 않습니다.
이는 초안 문서로서 언제든지 다른 문서에 의해 업데이트, 대체, 혹은 폐기될 수 있습니다. 진행 중인 작업으로서가 아닌 방식으로 이 문서를 인용하는 것은 부적절합니다.
이 문서는 W3C 특허 정책 하에서 활동하는 그룹이 제작했습니다. W3C는 이 그룹 산출물과 관련해 공개된 특허 공지의 공개 목록을 유지합니다. 해당 페이지에는 특허 공개 방법에 대한 지침도 포함되어 있습니다. 개인이 Essential Claim(s)을 포함한다고 믿는 특허를 실제로 알고 있는 경우 W3C 특허 정책 6절에 따라 정보를 공개해야 합니다.
이 문서는 2025년 8월 18일 W3C 프로세스 문서에 의해 관리됩니다.
이 섹션은 비규범적입니다.
오늘날 대부분의 [HTML] 콘텐츠는 마우스 입력을 사용하거나 이를 위해 설계되었습니다. 입력을 사용자 정의 방식으로 처리하는 경우 일반적으로 [UIEVENTS] 마우스 이벤트에 맞춰 코드를 작성합니다. 그러나 최신 컴퓨팅 기기들은 터치스크린 및 펜 입력을 포함한 다른 형태의 입력을 통합하고 있습니다. 이러한 각 입력 형태를 개별적으로 처리하기 위한 이벤트 타입들이 제안되었습니다. 하지만 이 접근 방식은 새 입력 타입을 지원할 때 종종 불필요한 로직 및 이벤트 처리 오버헤드 중복을 야기합니다. 이는 콘텐츠가 한 가지 기기 타입만을 염두에 두고 작성될 때 호환성 문제를 자주 발생시킵니다. 또한 기존 마우스 기반 콘텐츠와의 호환성을 위해 대부분의 user agents는 모든 입력 타입에 대해 마우스 이벤트를 발생시킵니다. 이로 인해 특정 마우스 이벤트가 실제 마우스 장치를 나타내는 것인지, 호환성을 위해 다른 입력 타입으로부터 생성된 것인지 모호해져 두 기기 타입을 동시에 대상으로 코딩하기 어려워집니다.
다중 입력 타입에 대한 코딩 비용을 줄이고 위에서 설명한 마우스 이벤트의 모호성을 해결하기 위해 이 명세는 보다 추상화된 입력 형태인 포인터(pointer)를 정의합니다. 포인터는 마우스 커서, 펜, 터치(멀티터치 포함) 또는 기타 포인팅 입력 장치에 의해 화면에 만들어지는 임의의 접촉 지점일 수 있습니다. 이 모델은 사용자가 어떤 하드웨어를 가지고 있든 잘 동작하는 사이트 및 애플리케이션을 더 쉽게 작성할 수 있게 해 줍니다. 기기별 처리가 필요한 시나리오를 위해 이 명세는 이벤트를 생성한 기기 타입을 검사할 수 있는 속성도 정의합니다. 주된 목표는 다양한 기기에서의 포인터 입력을 교차 지원하는 단일 이벤트/인터페이스 집합을 제공하면서도 향상된 경험을 위해 필요한 경우에만 기기별 처리를 허용하는 것입니다.
추가적인 핵심 목표는 멀티 스레드 사용자 에이전트가 스크립트 실행에 블로킹되지 않고 패닝 및 줌(예: 터치스크린에서 손가락이나 스타일러스)과 같은 직접 조작 동작을 처리할 수 있도록 하는 것입니다.
이 명세는 다양한 포인터 입력에 대한 통합 이벤트 모델을 정의하지만, 키보드나 키보드와 유사한 인터페이스(예: 터치스크린 전용 기기에서 실행되는 화면 낭독기나 유사한 보조 기술로, 포커스 가능한 컨트롤 및 요소를 순차적으로 탐색하도록 하는 인터페이스)와 같은 다른 형태의 입력은 다루지 않습니다. 사용자 에이전트가 이러한 인터페이스에 대한 응답으로 포인터 이벤트를 추가로 생성하도록 선택할 수는 있지만, 이 시나리오는 본 명세에서 다루지 않습니다.
첫 단계로 작성자들은 focus, blur,
click과 같은 고수준 이벤트에 응답함으로써 모든 형태의 입력에 대해 동등한 기능을 제공하도록 권장됩니다. 그러나 저수준 이벤트(예: Pointer
Events)를 사용할 때에는 모든 입력 타입이 지원되도록 하는 것이 바람직합니다. 키보드 및 키보드 유사 인터페이스의 경우 명시적인 키보드 이벤트 처리가 추가로 필요할 수
있습니다. 자세한 내용은 Keyboard Accessible
[WCAG22]를 참조하십시오.
범용 포인터 입력을 처리하는 이벤트는 마우스 이벤트와 매우 유사합니다: pointerdown,
pointermove, pointerup, pointerover, pointerout,
등등. 이는 마우스 이벤트에서 포인터 이벤트로 콘텐츠를 쉽게 마이그레이션할 수 있게 합니다.
포인터 이벤트는 (클라이언트 좌표, 대상 요소, 버튼 상태 포함) 마우스 이벤트에 존재하는 일반적인 모든 속성에 더해 압력, 접촉 기하, 기울기와 같은 다른 입력 형태를 위한 새 속성도
제공합니다. 작성자는 의미 있는 곳에서는 서로 다른 입력 타입 사이에서 로직을 공유하고, 최상의 경험을 위해 필요한 경우에만 특정 입력 타입에 맞춰 커스터마이징할 수 있습니다.
포인터 이벤트가 다양한 입력 장치로부터 발생하더라도, 다른 기기별 이벤트 집합으로부터 생성된 것으로 정의되지는 않습니다. 호환성을 위해 가능하며 권장되지만, 이 명세는 (마우스 이벤트나 터치 이벤트와 같은) 다른 기기별 이벤트 지원을 요구하지 않습니다. 사용자 에이전트는 어떤 다른 기기 이벤트도 지원하지 않고 포인터 이벤트만 지원할 수도 있습니다. 마우스 전용 이벤트에 맞춰 작성된 콘텐츠와의 호환성을 위해 이 명세는 마우스가 아닌 장치의 포인터 입력을 기반으로 호환성 마우스 이벤트를 생성하는 방법을 설명하는 선택적 섹션을 제공합니다.
이 명세는 [TOUCH-EVENTS]에 정의된 터치 이벤트와 포인터 이벤트를 모두 지원하는 사용자 에이전트의 예상 동작에 대한 어떠한 조언도 제공하지 않습니다. 두 명세 간 관계에 대한 추가 정보는 Touch Events 커뮤니티 그룹을 참조하십시오.
비규범적이라고 표시된 섹션뿐 아니라, 이 명세의 모든 작성 지침, 다이어그램, 예시, 그리고 노트는 비규범적입니다. 이 명세의 나머지 모든 것은 규범적입니다.
이 문서에서 MAY, MUST, MUST NOT, OPTIONAL, SHOULD라는 키워드는 BCP 14 [RFC2119] [RFC8174]에 따라, 여기에서처럼 모두 대문자로 나타나는 경우에 한하여 해석되어야 합니다.
이 섹션은 비규범적입니다.
아래는 이 명세의 일부 API가 작성자에 의해 어떻게 사용될 수 있는지 보여주는 기본적인 예시들입니다. 더 구체적인 추가 예시는 이 문서의 관련 섹션들에 제공됩니다.
/* Pointer Events 또는 기존 touch/mouse 중 하나에 바인딩 */
if (window.PointerEvent) {
// Pointer Events가 지원되면 포인터 이벤트만 리슨
target.addEventListener("pointerdown", function(e) {
// 필요하다면 e.pointerType에 따라 별도 로직 적용
// 서로 다른 touch/pen/mouse 동작 구분
...
});
...
} else {
// 기존 touch/mouse 이벤트 핸들러
target.addEventListener('touchstart', function(e) {
// 호환성 마우스 이벤트 및 click 방지
e.preventDefault();
...
});
...
target.addEventListener('mousedown', ...);
...
}
// 키보드 처리를 위한 추가 이벤트 리스너
...
window.addEventListener("pointerdown", detectInputType);
function detectInputType(event) {
switch(event.pointerType) {
case "mouse":
/* 마우스 입력 감지됨 */
break;
case "pen":
/* 펜/스타일러스 입력 감지됨 */
break;
case "touch":
/* 터치 입력 감지됨 */
break;
default:
/* pointerType이 비어 있음(감지 불가) 또는 UA 고유 커스텀 타입 */
}
}
<div style="position:absolute; top:0px; left:0px; width:100px;height:100px;"></div>
<script>
window.addEventListener("pointerdown", checkPointerSize);
function checkPointerSize(event) {
event.target.style.width = event.width + "px";
event.target.style.height = event.height + "px";
}
</script>
const event1 = new PointerEvent("pointerover",
{ bubbles: true,
cancelable: true,
composed: true,
pointerId: 42,
pointerType: "pen",
clientX: 300,
clientY: 500
});
eventTarget.dispatchEvent(event1);
let pointerEventInitDict =
{
bubbles: true,
cancelable: true,
composed: true,
pointerId: 42,
pointerType: "pen",
clientX: 300,
clientY: 500,
};
const p1 = new PointerEvent("pointermove", pointerEventInitDict);
pointerEventInitDict.clientX += 10;
const p2 = new PointerEvent("pointermove", pointerEventInitDict);
pointerEventInitDict.coalescedEvents = [p1, p2];
const event2 = new PointerEvent("pointermove", pointerEventInitDict);
eventTarget.dispatchEvent(event2);
<div style="position:absolute; top:0px; left:0px; width:100px;height:100px;"></div>
<script>
window.addEventListener("pointerdown", assignPenColor);
window.addEventListener("pointermove", assignPenColor);
const colorMap = new Map();
function assignPenColor(event) {
const uniqueId = event.persistentDeviceId;
// 고유 ID 존재 여부 확인
if (uniqueId == 0) {
return;
}
// 기기에 색상이 이미 할당되었는지 확인
if (map.has(uniqueId)) {
return;
}
// 기기에 색상 할당
let newColor = getNewColor();
map.set(uniqueId, newColor);
return newColor;
}
function getNewColor() {
/* 어떤 색상 값 반환 */
}
</script>
WebIDLdictionary PointerEventInit : MouseEventInit {
long pointerId = 0;
double width = 1;
double height = 1;
float pressure = 0;
float tangentialPressure = 0;
long tiltX;
long tiltY;
long twist = 0;
double altitudeAngle;
double azimuthAngle;
DOMString pointerType = "";
boolean isPrimary = false;
long persistentDeviceId = 0;
sequence<PointerEvent> coalescedEvents = [];
sequence<PointerEvent> predictedEvents = [];
};
[Exposed=Window]
interface PointerEvent : MouseEvent {
constructor(DOMString type, optional PointerEventInit eventInitDict = {});
readonly attribute long pointerId;
readonly attribute double width;
readonly attribute double height;
readonly attribute float pressure;
readonly attribute float tangentialPressure;
readonly attribute long tiltX;
readonly attribute long tiltY;
readonly attribute long twist;
readonly attribute double altitudeAngle;
readonly attribute double azimuthAngle;
readonly attribute DOMString pointerType;
readonly attribute boolean isPrimary;
readonly attribute long persistentDeviceId;
[SecureContext] sequence<PointerEvent> getCoalescedEvents();
sequence<PointerEvent> getPredictedEvents();
};
pointerId이 이벤트를 발생시키는 포인터에 대한 고유 식별자입니다. user agents는 기본 마우스 포인터용으로 일반적인 pointerId
값 0 또는 1을 예약할 수 MAY 있습니다. pointerId
값 -1은 포인팅 장치 이외에 의해 생성된 이벤트를 나타내기 위해 예약되고 사용되어야 MUST
합니다. 기타 포인터에 대해서는 user agent가 pointerId 값을 할당하는 전략을 자유롭게
구현할 수 있습니다. 그러나 모든 active pointers는 최상위 탑
레벨 브라우징 컨텍스트 내에서 유일해야 하며 다른 탑 레벨 브라우징 컨텍스트에 의해 영향을 받아서는 MUST
NOT 합니다(즉 한 브라우징 컨텍스트 밖으로 이동한다고 동일 값일 것이라 가정할 수 없음).
user agent는 이전에
사용 종료된(active에서 벗어난) pointerId 값을 재활용할 수 MAY 있고 특정 포인팅 장치에 대해 항상 동일 pointerId를 재사용할 수도 MAY 있습니다(예: 다중 사용자 협업 앱에서 특정 펜/스타일러스 입력을 고유하게 식별). 그러나 후자의 경우 다른
페이지/도메인 간 핑거프린팅 및 추적 가능성을 최소화하기 위해 그 pointerId는 페이지/세션 수명 동안에만
명시적으로 연관되어야 MUST 하며 새 세션에서 다시 사용될 때는 새로운 무작위 pointerId를 선택해야 MUST 합니다.
pointerId 선택 알고리즘은
구현별입니다. 작성자는 값이 고유 식별자 이상의 의미를 가진다고 가정해서는 안 됩니다. 예를 들어 user agent가 단순히 활성화 순서대로
0부터 번호를 붙일 수 있지만 반드시 단조 증가를 보장하지는 않습니다. 특정 장치에 대해 동일 pointerId를 재사용할지 여부는
개별 구현에 달려 있으므로 이에 의존하지 않는 것이 좋고 대신 persistentDeviceId를
참조하는 것이 권장됩니다.
width포인터의 contact geometry X축 크기(폭)를 CSS 픽셀 단위( [CSS21]
참조)로 나타냅니다. 이 값은 주어진 포인터에 대해 각 이벤트마다 갱신될 수 MAY 있습니다. 접촉 기하가 일반적으로
없는 입력(전통적인 마우스 등)이나 하드웨어가 실제 기하를 감지하지 못하는 경우 user agent는 기본값 1을
반환해야 MUST 합니다.
height포인터의 contact geometry Y축 크기(높이)를 CSS 픽셀 단위로 나타냅니다. 갱신
가능성과 기본값 처리 방식은 width와 동일합니다. 접촉 기하가 없거나 감지되지 않으면 1을 반환해야 MUST 합니다.
pressure포인터 입력의 정규화된 압력으로 범위는 [0,1]이며 각각 하드웨어가 감지 가능한 최소/최대 압력을 의미합니다. 압력을 지원하지 않는
하드웨어/플랫폼에서는 active buttons state일 때 0.5, 그
외에는 0이어야 MUST 합니다.
tangentialPressure추가 컨트롤(예: 에어브러시 스타일러스의 손가락 휠)에 의해 설정되는 정규화된 접선 압력(배럴 압력)으로 범위는 [-1,1],
0은 중립 위치입니다. 일부 하드웨어는 [0,1] 양수 범위만 지원할 수 있습니다. 지원하지 않는 경우 값은
0이어야 MUST 합니다.
tiltXY-Z 평면과 변환기(펜/스타일러스) 축 및 Y축을 포함하는 평면 사이의 평면 각(도 단위, [-90,90] 범위)입니다. 양의
tiltX는 X 증가 방향(오른쪽)입니다. tiltX와 tiltY를 함께 사용하여 디지타이저에
대한 변환기의 기울기를 나타냅니다. 기울기/각도를 보고하지 않는 하드웨어/플랫폼에서는 0이어야 MUST 합니다.
tiltX 예.
tiltYX-Z 평면과 변환기 축 및 X축을 포함하는 평면 사이의 평면 각(도 단위, [-90,90] 범위)입니다. 양의
tiltY는 사용자 방향(Y 증가)입니다. 나머지 의미와 사용은 tiltX 설명과 동일하며 보고되지 않는 경우
0이어야 MUST 합니다.
tiltY 예.
twist변환기(펜/스타일러스)의 주축을 기준으로 한 시계 방향 회전 각도(도 단위, [0,359] 범위)입니다. twist를 보고하지 않는 경우 값은
0이어야 MUST 합니다.
altitudeAngle변환기(펜/스타일러스)의 고도(라디안)로 범위는 [0,π/2]입니다. 0은 표면(X-Y 평면)과 평행,
π/2는 표면에 수직입니다. 기울기/각도를 보고하지 않는 하드웨어/플랫폼에서는 π/2이어야 MUST 합니다.
altitudeAngle 기본값은 π/2로 변환기를 표면에 수직 위치로 둡니다. 이는 Touch Events - Level 2 명세에서
altitudeAngle 기본값이 0인 것과 다릅니다.
altitudeAngle
π/4 (X-Y 평면으로부터 45도) 예.azimuthAngle변환기(펜/스타일러스)의 방위각(라디안)으로 범위는 [0, 2π]입니다. 0은 변환기 캡이 X 증가 방향("정면에서
내려다볼 때 3시")을 가리키는 상태이며 시계 방향으로 값이 증가합니다(π/2=6시, π=9시,
3π/2=12시). 변환기가 표면에 완전히 수직(altitudeAngle=π/2)이면 값은
0이어야 MUST. 기울기/각도를 보고하지 않는 경우도 0이어야 MUST 합니다.
azimuthAngle
π/6("4시") 예.pointerType이벤트를 발생시킨 장치 유형(예: mouse, pen, touch)을 나타냅니다. user agent가 마우스, 펜/스타일러스 또는 터치 입력 장치에 대해 pointer event를 발생시키는 경우
pointerType 값은 아래 표에 따라 설정되어야 MUST 합니다:
| 포인터 장치 유형 | pointerType 값 |
|---|---|
| Mouse | mouse |
| Pen / stylus | pen |
| Touch contact | touch |
장치 유형을 user agent가 감지하지 못하면 값은 빈 문자열이어야 MUST 합니다. 위 목록 외 추가 포인터 장치
유형을 지원하는 경우 pointerType 값은 충돌을 피하기 위해 벤더 프리픽스를 붙이는 것이 SHOULD 입니다. 이후 명세에서 다른 장치 유형에 대한 추가 규범적 값이 제공될 수 MAY 있습니다.
pointerType 기본
사용을 확인할 수 있습니다. 또한 user agent가 자체 커스텀 pointerType 값을 구현했거나 빈 문자열인 경우를 처리할
기본 로직을 포함해야 합니다.isPrimary이 포인터가 해당 포인터 유형의 primary pointer인지 여부를 나타냅니다.
persistentDeviceId포인팅 장치 자체의 고유 식별자입니다. 하드웨어가 다중 포인터를 지원하는 경우, 포인터들이 세션 전체에서 고유하게 식별 가능한 경우에만
persistentDeviceId를 부여해야 MUST 합니다. 고유하게 식별 가능한 경우 해당
포인팅 장치에 할당된 persistentDeviceId는 세션 종료까지 유지됩니다. 값 0은 생성 장치를 식별할 수
없음을 나타내기 위해 예약되고 사용되어야 MUST 합니다. pointerId와 마찬가지로 핑거프린팅/추적 위험을 줄이기 위해
페이지/세션 수명 동안에만 명시적으로 연관되어야 MUST 하며 새 세션에서는 새 무작위
persistentDeviceId를 선택해야 MUST 합니다.
persistentDeviceId가 항상
제공된다고 보장할 수 없습니다. 예: 디바이스가 pointerdown 시점까지 하드웨어 id를 보고하지 못해 초기에는
0이었다가 이후 유효한 값으로 변경될 수 있습니다.
getCoalescedEvents()coalesced events 목록을 반환하는 메서드입니다.
getPredictedEvents()predicted events 목록을 반환하는 메서드입니다.
PointerEventInit 딕셔너리는 PointerEvent 인터페이스 생성자가 신뢰되지 않는(합성된) 포인터 이벤트를
구성하는 메커니즘을 제공합니다. 이는 [UIEVENTS]에 정의된 MouseEventInit
딕셔너리를 상속합니다. 합성 포인터 이벤트를 발생시키는 예시는 examples 섹션을 참조하세요.
event
constructing steps는 PointerEvent 생성 시 PointerEventInit의 coalescedEvents를 coalesced events list로 복제하고, PointerEventInit의 predictedEvents를 predicted events list로 복제합니다.
PointerEvent 인터페이스는 UI
Events에 정의된 MouseEvent를 상속합니다. 또한
CSSOM
View Module에서 제안된 확장(좌표 속성을 long에서 double로 변경)을 PointerEvent에는 구현했지만 일반 MouseEvent에는 적용하지 않은 user
agent라면 click,
auxclick, contextmenu 이벤트 처리 시 추가 요구사항이 있습니다.
다중 포인터(예: 멀티터치) 시나리오에서 isPrimary 속성은 각 포인터 유형의 active pointers
집합 중 대표(master) 포인터를 식별하는 데 사용됩니다.
pointerType별 포인터가 primary로 간주됩니다. 예: 터치 접촉과
마우스 커서를 동시에 이동하면 두 포인터 모두 primary입니다.isPrimary값이 false인 이벤트를 발생시킬 수 MAY 있습니다.e라는 이름의 포인터 이벤트를 fire a
pointer event 한다는 것은 fire an event 알고리즘을 사용하여 PointerEvent 인터페이스 및 PointerEvent Interface와 Attributes and Default Actions에 정의된 대로 속성이
설정된 이벤트를 발행하는 것을 의미합니다.
이벤트가 gotpointercapture, lostpointercapture, click,
auxclick, contextmenu가 아니라면 해당 PointerEvent에 대해 process pending pointer capture 단계를
수행합니다.
Determine the target 단계는 다음과 같이 이벤트가 발행될 대상(target)을 결정합니다:
targetDocument를 target의 node document [DOM]으로 둡니다.
이벤트가 pointerdown, pointermove, 또는 pointerup이면 해당 이벤트의 pointerId에 대한 active document를
targetDocument로 설정합니다.
이벤트가 pointerdown이고 관련 장치가 직접 조작(direct manipulation)
장치이며 target이 Element인 경우 set pointer capture를 수행하여 pointerId를 해당 요소에 캡처합니다(implicit pointer capture 참고).
이 이벤트를 발행하기 전에 user
agent는 이벤트 순서를 보장하기 위해 (event ordering
[UIEVENTS]) 포인터가 previousTarget에서 target으로 이동한 것처럼
처리해야 SHOULD 하며 needsOverEvent 플래그가 설정된 경우 동일 요소라도 pointerover 이벤트가 필요합니다.
결정된 target에 이벤트를 발행(Fire)합니다.
결정된 target을 해당 포인터의 previousTarget으로 저장하고 needsOverEvent 플래그를
false로 재설정합니다.
이후 previousTarget이 더 이상 connected [DOM] 상태가 아니게 되면 이벤트
경로를 따라 디스패치 시 가장 가까운 연결된 부모로 previousTarget을 갱신하고 needsOverEvent를
true로 설정합니다.
이 명세에서 정의된 이벤트 타입에 대한 bubbles, cancelable 속성과 기본 동작은 아래 표에 나타납니다. 각 이벤트
타입 상세는 Pointer Event types를 참고하세요.
| Event Type | Bubbles | Cancelable | Default Action |
|---|---|---|---|
pointerover |
Yes | Yes | None |
pointerenter |
No | No | None |
pointerdown |
Yes | Yes | 변동: 포인터가 primary일 때 mousedown의 모든 기본 동작이 이벤트를 취소하면 이후 compatibility mouse events 발생을 방지합니다. |
pointermove |
Yes | Yes | 변동: 포인터가 primary일 때 mousemove 기본 동작 |
pointerrawupdate |
Yes | No | None |
pointerup |
Yes | Yes | 변동: 포인터가 primary일 때 mouseup 기본 동작 |
pointercancel |
Yes | No | None |
pointerout |
Yes | Yes | None |
pointerleave |
No | No | None |
gotpointercapture |
Yes | No | None |
lostpointercapture |
Yes | No | None |
뷰포트 조작(패닝·줌)은 일반적으로 direct manipulation 상호작용 결과이지만 포인터 이벤트의 기본 동작이
아니므로(일부 예: 손가락 이동에 따른 페이지 패닝) 해당 포인터 이벤트를 취소해도 억제되지 않습니다. 대신 문서 영역에 대해
touch-action을 사용하여 직접 조작 동작을 선언해야 합니다.
이벤트 취소 의존성을 제거하면 user agent가 성능 최적화를 수행하기 용이합니다.
pointerenter, pointerleave 이벤트의 composed
[DOM] 속성은 false이어야 SHOULD 하며, 위 표의 다른 모든 포인터 이벤트는 true이어야 SHOULD 합니다.
위 표의 모든 포인터 이벤트에서 detail [UIEVENTS] 속성은 0이어야 SHOULD 합니다.
fromElement,
toElement를 노출합니다. PointerEvents에서는 이러한 (상속된) 속성을 null로 설정하여 표준
대안(target, relatedTarget) 사용으로 전환하는 것을 권장합니다.
MouseEvent의 relatedTarget과
유사하게 relatedTarget은 pointerover /
pointerenter에서는 포인터가 막 떠난 요소, pointerout / pointerleave에서는 포인터가 들어가는 요소로 초기화됩니다. 다른
포인터 이벤트에서는 기본값이 null입니다. 요소가 포인터 캡처를 획득하면 이후 해당 포인터의 모든 이벤트는 캡처 요소 경계 내부로 간주됩니다.
gotpointercapture 및 lostpointercapture 이벤트는 표에 정의된 것 제외
모든 속성이 user agent가 process pending
pointer capture 단계를 실행하고 gotpointercapture/lostpointercapture 이벤트를 발행하게 한 그 포인터
이벤트와 동일해야 합니다.
user agent는 implicitly releasing pointer
capture 시점과 gotpointercapture 또는 lostpointercapture가 아닌 포인터 이벤트를 발행할 때
아래 단계를 수행해야 MUST 합니다:
lostpointercapture 이벤트를 해당 pointer capture target override
노드에 발행합니다.gotpointercapture 이벤트를 pending
pointer capture target override에 발행합니다.click,
auxclick, contextmenu 이벤트 정의에 따라 lostpointercapture가 디스패치된 후에도
해당 click / auxclick / contextmenu 이벤트가 존재한다면 캡처
대상에 계속 디스패치될 수 있습니다.
user agent는 특정 pointerId에 대한 포인터 이벤트를 웹 페이지가 더
이상 계속 수신하지 않을 것으로 판단하면 suppress a pointer event stream을 수행해야 MUST 합니다. 다음 시나리오 중 하나가 조건을 만족합니다(추가 시나리오가 있을 수 MAY 있음):
touch-action CSS 속성 섹션을 참조하세요.
user agent가 추가로 포인터 이벤트 스트림을 억제할 수 있는 다른 상황:
이러한 시나리오를 감지하는 방법은 명세 범위를 벗어납니다.
user agent는 포인터 이벤트 스트림을 억제하기 위해 다음 단계를 수행해야 MUST 합니다:
pointercancel 이벤트 발행.pointerout 이벤트 발행.pointerleave 이벤트 발행.포인팅 장치가 화면 표면 대비 이동하거나 속성 변화가 발생하면 Pointer Event types에 정의된 다양한
이벤트가 발생합니다. 위치·속성 변화가 전혀 없는 정지 포인팅 장치에서 레이아웃 변경이 포인터의 hit test 대상에 영향을 주면 user agent는 pointerover, pointerenter, pointerout, pointerleave 이벤트를 발생시켜야 MUST 합니다. 성능상의 이유로 이러한 경계 이벤트 발행을 지연시킬 수 MAY
있습니다(예: hit-test 과다 또는 경계 이벤트 리스너로 인한 레이아웃 변화 방지).
pointermove 이벤트를 발생시키지 않습니다.Pointer Events는 변환기의 X-Y 평면 대비 방향을 표현하기 위해 두 세트(tiltX/tiltY: 원래 명세 도입,
azimuthAngle/altitudeAngle: Touch Events - Level 2에서 채택)를 제공합니다.
특정 하드웨어·플랫폼에 따라 user agent는 변환기 방향에 대해 두 세트 중 하나만 수신하는 경우가 많으며(tiltX/tiltY
또는 altitudeAngle/azimuthAngle) user agent는 아래 변환 알고리즘을 사용해야 MUST 합니다.
user agent가 azimuthAngle/altitudeAngle으로부터
tiltX/tiltY를 계산할 때 최종 정수 값은 Math.round [ECMASCRIPT] 규칙을 사용해 반올림해야 SHOULD 합니다.
/* Converting between tiltX/tiltY and altitudeAngle/azimuthAngle */
function spherical2tilt(altitudeAngle, azimuthAngle) {
const radToDeg = 180/Math.PI;
let tiltXrad = 0;
let tiltYrad = 0;
if (altitudeAngle == 0) {
// the pen is in the X-Y plane
if (azimuthAngle == 0 || azimuthAngle == 2*Math.PI) {
// pen is on positive X axis
tiltXrad = Math.PI/2;
}
if (azimuthAngle == Math.PI/2) {
// pen is on positive Y axis
tiltYrad = Math.PI/2;
}
if (azimuthAngle == Math.PI) {
// pen is on negative X axis
tiltXrad = -Math.PI/2;
}
if (azimuthAngle == 3*Math.PI/2) {
// pen is on negative Y axis
tiltYrad = -Math.PI/2;
}
if (azimuthAngle>0 && azimuthAngle<Math.PI/2) {
tiltXrad = Math.PI/2;
tiltYrad = Math.PI/2;
}
if (azimuthAngle>Math.PI/2 && azimuthAngle<Math.PI) {
tiltXrad = -Math.PI/2;
tiltYrad = Math.PI/2;
}
if (azimuthAngle>Math.PI && azimuthAngle<3*Math.PI/2) {
tiltXrad = -Math.PI/2;
tiltYrad = -Math.PI/2;
}
if (azimuthAngle>3*Math.PI/2 && azimuthAngle<2*Math.PI) {
tiltXrad = Math.PI/2;
tiltYrad = -Math.PI/2;
}
}
if (altitudeAngle != 0) {
const tanAlt = Math.tan(altitudeAngle);
tiltXrad = Math.atan(Math.cos(azimuthAngle) / tanAlt);
tiltYrad = Math.atan(Math.sin(azimuthAngle) / tanAlt);
}
return {"tiltX":tiltXrad*radToDeg, "tiltY":tiltYrad*radToDeg};
}
function tilt2spherical(tiltX, tiltY) {
const tiltXrad = tiltX * Math.PI/180;
const tiltYrad = tiltY * Math.PI/180;
// calculate azimuth angle
let azimuthAngle = 0;
if (tiltX == 0) {
if (tiltY > 0) {
azimuthAngle = Math.PI/2;
}
else if (tiltY < 0) {
azimuthAngle = 3*Math.PI/2;
}
} else if (tiltY == 0) {
if (tiltX < 0) {
azimuthAngle = Math.PI;
}
} else if (Math.abs(tiltX) == 90 || Math.abs(tiltY) == 90) {
// not enough information to calculate azimuth
azimuthAngle = 0;
} else {
// Non-boundary case: neither tiltX nor tiltY is equal to 0 or +-90
const tanX = Math.tan(tiltXrad);
const tanY = Math.tan(tiltYrad);
azimuthAngle = Math.atan2(tanY, tanX);
if (azimuthAngle < 0) {
azimuthAngle += 2*Math.PI;
}
}
// calculate altitude angle
let altitudeAngle = 0;
if (Math.abs(tiltX) == 90 || Math.abs(tiltY) == 90) {
altitudeAngle = 0
} else if (tiltX == 0) {
altitudeAngle = Math.PI/2 - Math.abs(tiltYrad);
} else if (tiltY == 0) {
altitudeAngle = Math.PI/2 - Math.abs(tiltXrad);
} else {
// Non-boundary case: neither tiltX nor tiltY is equal to 0 or +-90
altitudeAngle = Math.atan(1.0/Math.sqrt(Math.pow(Math.tan(tiltXrad),2) + Math.pow(Math.tan(tiltYrad),2)));
}
return {"altitudeAngle":altitudeAngle, "azimuthAngle":azimuthAngle};
}
아래는 이 명세에서 정의된 이벤트 타입입니다.
primary pointer의 경우, 다음 이벤트들은(gotpointercapture 및 lostpointercapture는 예외) compatibility mouse events도 발생시킬 수 있습니다.
user agent는 다음 상황 중
하나가 발생하면 MUST로 이름이 pointerover인 포인터 이벤트를 발행해야 합니다:
pointerdown 이벤트를 (아래의 does not support hover 장치에 대해)
발행하기 전에.user agent는 다음 상황 중
하나가 발생하면 MUST로 이름이 pointerenter인 포인터 이벤트를 발행해야 합니다:
pointerdown 이벤트를 (아래의 does not support hover 장치에 대해)
발행하기 전에.pointerover와 유사하지만 두 가지 차이가 있습니다. pointerenter는 버블링하지 않으며, 디스패치 시 하위 요소들의 히트
테스트 경계까지 고려합니다.mouseenter 이벤트 및 [CSS21]에
설명된 CSS :hover 의사 클래스와 유사점이 있습니다. pointerleave 이벤트도
참고하세요.user agent는 포인터가 active buttons state에 들어갈 때 MUST로 이름이 pointerdown인 포인터 이벤트를 발행해야 합니다. 마우스의 경우 버튼이
없음에서 하나 이상 눌림으로 전이될 때, 터치의 경우 digitizer에 물리적 접촉이 이루어질 때, 펜의 경우 버튼을 누르지 않은 채 디지타이저에 닿거나 호버 중
버튼 없음에서 하나 이상 눌림으로 전이될 때입니다.
pointerdown 및 pointerup이 mousedown 및
mouseup과 동일한 모든 상황에서 발생하지 않음을 의미합니다. 자세한 내용은 chorded
buttons를 참조하세요.
devices that do not support hover 입력의
경우, user agent는 MUST로 이름이 pointerover인 포인터 이벤트를 먼저 발행하고, 이어서 pointerenter 이벤트를 발행한 후 pointerdown 이벤트를 디스패치해야 합니다.
isPrimary 속성이 true인 경우) compatibility mouse events 중 일부의 발생을 pointerdown 이벤트를 취소하여 방지할 수 있습니다. 이렇게 하면 해당
포인터에 PREVENT MOUSE EVENT 플래그가 설정됩니다. 단, 이는 mouseover,
mouseenter, mouseout, mouseleave 이벤트의 발생은 막지 못합니다.
user agent는 pointerdown 또는 pointerup 이벤트를 발생시키지 않는 속성들에 변화가 있을 때 MUST로 이름이 pointermove인 포인터 이벤트를 발행해야 합니다. 여기에는 좌표,
pressure, tangential pressure, tilt, twist, 접촉 기하(width, height) 또는 chorded buttons의 변경이 포함됩니다.
user agent는 (예: 성능상의 이유로) pointermove 이벤트의 디스패치를 MAY로 지연할 수 있습니다.
이러한 경우 coalesced events 정보는 단일로 디스패치된 pointermove 이벤트의 getCoalescedEvents 메서드를 통해
제공됩니다.
이때 이벤트의 최종 좌표를 이벤트 대상 결정을 위한 좌표로 사용해야 합니다.
user agent는 MUST로 이름이
pointerrawupdate인 포인터 이벤트를 발행해야 하며, 이는 반드시
secure context
내에서만 이루어져야 합니다. 이 이벤트는 pointerdown 또는 pointerup을 발생시키지 않는 속성들에 변화가 있을 때
발생합니다. 해당 속성 목록은 pointermove를 참고하세요.
pointermove와 달리, user agent는 SHOULD로 pointerrawupdate 이벤트를
가능한 한 빨리, 그리고 자바스크립트가 처리할 수 있는 빈도만큼 자주 디스패치해야 합니다.
pointerrawupdate 이벤트의 target은
pointermove 이벤트의 target과 다를 수
있습니다. 이는 pointermove 이벤트가 지연되거나 병합(coalesce)될 수 있으며, 이벤트
대상 결정을 위한 최종 위치가 병합된 이벤트들의 위치와 다를 수 있기 때문입니다.
이미 동일한 pointerId를 가진, 아직 event loop에서 디스패치되지
않은 또 다른 pointerrawupdate가 있는 경우, user agent는 새로운 pointerrawupdate를 생성하는 대신 해당 이벤트와 병합할 수 MAY 있습니다. 이는 새로운 task 생성을 피하기
위함입니다.
이로 인해 pointerrawupdate가 coalesced events를 가지게 될 수
있으며,
이러한 이벤트들은 coalesced events로 하나의 pointerrawupdate 이벤트에 묶여, 해당 이벤트가 event loop에서 처리되는
즉시 전달됩니다.
자세한 내용은 getCoalescedEvents를 참고하세요.
pointerrawupdate와 pointermove의 순서와 관련하여,
플랫폼으로부터의 업데이트가 두 이벤트(pointerrawupdate, pointermove) 모두를 유발하는 경우,
user agent는 MUST로 해당 pointerrawupdate를 대응하는
pointermove보다 먼저 디스패치해야 합니다.
target을 제외하면, 마지막 pointermove 이벤트 이후에 디스패치된 모든 pointerrawupdate의 coalesced events 리스트들을
연결(concatenate)한 결과는 다음 pointermove 이벤트의 coalesced events와 (다른 이벤트 속성
측면에서) 동일합니다.
pointerrawupdate의 속성은 pointermove와 대부분 동일하나, cancelable은
pointerrawupdate의 경우 MUST로 false여야 합니다.
user agent는 SHOULD로 compatibility mouse events를 pointerrawupdate에 대해 발생시키지 않아야 합니다.
pointerrawupdate 이벤트 리스너를 추가하면 user
agent 구현에 따라 웹 페이지 성능에 부정적 영향을 줄 수 있습니다.
대부분의 사용 사례에서는 다른 pointer 이벤트 타입들로 충분합니다.
자바스크립트가 매우 높은 빈도의 이벤트를 필요로 하고 그만큼 빠르게 처리할 수 있을 때에만 pointerrawupdate 리스너를 추가하세요.
이러한 경우에는 다른 포인터 이벤트 타입을 들을 필요가 없을 수 있습니다.user agent는 포인터가 active buttons state를 벗어날 때 MUST로 이름이 pointerup인 포인터 이벤트를 발행해야 합니다. 마우스의 경우 하나 이상 눌림에서
버튼이 없음으로 전이, 터치의 경우 digitizer로부터 물리적 접촉이 제거될 때, 펜의 경우 버튼이 눌리지 않은 상태에서 디지타이저와의 접촉이
제거되거나, 호버 중 하나 이상 눌림에서 버튼이 없음으로 전이될 때입니다.
devices that do not support hover 입력의
경우, user agent는 MUST로 이름이 pointerout인 포인터 이벤트를 발행하고, 이어서 pointerleave 이벤트를 pointerup 이벤트를 디스패치한 후에 발행해야 합니다.
모든 pointerup 이벤트의 pressure 값은
0입니다.
user agent는 포인터가 현재 캡처된 상태라면 MUST로 implicitly release the pointer capture를 수행해야 합니다.
pointerdown 및 pointerup이 mousedown 및
mouseup과 동일한 모든 상황에서 발생하지 않음을 의미합니다. 자세한 내용은 chorded
buttons를 참조하세요.
user agent는 suppress a pointer event stream 상황을 감지하면
MUST로 이름이 pointercancel인 포인터 이벤트를 발행해야 합니다.
다음 속성들의 값은 pointercancel 이벤트에서 동일한 pointerId를 가진 마지막으로 디스패치된 포인터 이벤트의
값과 MUST로 일치해야 합니다: width,
height, pressure, tangentialPressure, tiltX,
tiltY, twist, altitudeAngle, azimuthAngle,
pointerType, isPrimary, 그리고 [UIEVENTS]에서 상속된
좌표들. 또한 pointercancel 이벤트의
coalescedEvents 및 predictedEvents 리스트는 MUST로 비어
있어야 하며, 이벤트의 cancelable 속성은 MUST로 false여야 합니다.
user agent는 다음 상황 중
하나가 발생하면 MUST로 이름이 pointerout인 포인터 이벤트를 발행해야 합니다:
pointerup 이벤트를 (아래의 does not support hover 장치에 대해) 발행한
후.user agent는 다음 상황 중
하나가 발생하면 MUST로 이름이 pointerleave인 포인터 이벤트를 발행해야 합니다:
pointerup 이벤트를 발행한 후.pointerout와 유사하지만 두 가지 차이가 있습니다. pointerleave는 버블링하지 않으며, 디스패치 시 하위 요소들의 히트
테스트 경계까지 고려합니다.mouseleave 이벤트 및 [CSS21]에
설명된 CSS :hover 의사 클래스와 유사점이 있습니다. pointerenter 이벤트도
참고하세요.user agent는 어떤 요소가 포인터
캡처를 받으면 MUST로 이름이 gotpointercapture인 포인터 이벤트를 발행해야 합니다. 이
이벤트는 포인터 캡처를 받는 그 요소에서 발생합니다. 이후 해당 포인터에 대한 이벤트들은 이 요소에서 발생합니다. setting pointer
capture 및 process pending pointer
capture 섹션을 참고하세요.
user agent는 포인터 캡처가 포인터에
대해 해제된 후 MUST로 이름이 lostpointercapture인 포인터 이벤트를 발행해야 합니다. 이
이벤트는 캡처가 제거된 그 요소에서 발생합니다. 캡처 해제 후 해당 포인터에 대한 이후의 모든 이벤트는 click,
auxclick, contextmenu 이벤트를 제외하고, 이벤트 대상 결정을 위해 정상적인 히트 테스트 메커니즘(본
명세 범위 밖)을 따릅니다. releasing pointer capture, implicit release of pointer
capture, process pending pointer
capture 섹션을 참고하세요.
다음 절은 포인터 캡처의 설정과 해제를 용이하게 하기 위해 기존 Element 인터페이스에 대한 확장을 설명합니다.
WebIDLpartial interface Element {
undefined setPointerCapture (long pointerId);
undefined releasePointerCapture (long pointerId);
boolean hasPointerCapture (long pointerId);
};
setPointerCapture()이 메서드가 호출된 요소에 대해, 인수 pointerId로
식별되는 포인터의 포인터 캡처를 설정합니다. 이후 해당 포인터의 이벤트는 포인터가 항상 캡처 대상 위에
있는 것처럼 정상 히트 테스트 결과 대신 캡처 대상을 사용하며, 캡처가 해제될 때까지 MUST 항상 이 요소를 대상으로 해야
합니다. 이 메서드가 효과를 가지려면 포인터는 active buttons state에 MUST 있어야 하며, 그렇지 않으면 조용히 실패합니다. 제공된 메서드 인수가 어떤 active pointers와도 일치하지 않으면, throw "NotFoundError" DOMException을
발생시킵니다.
releasePointerCapture()이 메서드가 호출된 요소로부터, 인수 pointerId로
식별되는 포인터의 포인터 캡처를 해제합니다. 이후 해당 포인터의 이벤트는 이벤트 대상 결정을 위해
정상적인 히트 테스트 메커니즘(본 명세 범위 밖)을 따릅니다. 제공된 메서드 인수가 어떤 active
pointers와도 일치하지 않으면, throw "NotFoundError" DOMException을
발생시킵니다.
hasPointerCapture이 메서드가 호출된 요소가 인수 pointerId로 식별되는 포인터에 대해 pointer capture를 가지고 있는지를 나타냅니다. 특히, pending pointer capture target
override가 pointerId에 대해 이 메서드가 호출된 요소로
설정되어 있으면 true를, 그렇지 않으면 false를 반환합니다.
setPointerCapture() 호출 직후
즉시 true를 반환하지만, 그 요소는 아직 gotpointercapture 이벤트를 받지 않았을 수
있습니다. 따라서 implicit pointer capture를 pointerdown 이벤트 리스너 내부에서 감지하는 데 유용할 수
있습니다.다음 절은 기존 GlobalEventHandlers
믹스인에 이벤트 핸들러 등록을 용이하게 하기 위한 확장을 설명합니다.
WebIDLpartial interface mixin GlobalEventHandlers {
attribute EventHandler onpointerover;
attribute EventHandler onpointerenter;
attribute EventHandler onpointerdown;
attribute EventHandler onpointermove;
[SecureContext] attribute EventHandler onpointerrawupdate;
attribute EventHandler onpointerup;
attribute EventHandler onpointercancel;
attribute EventHandler onpointerout;
attribute EventHandler onpointerleave;
attribute EventHandler ongotpointercapture;
attribute EventHandler onlostpointercapture;
};
onpointeroverpointerover 이벤트 타입을 위한 것입니다.
onpointerenterpointerenter 이벤트 타입을 위한 것입니다.
onpointerdownpointerdown 이벤트 타입을 위한 것입니다.
onpointermovepointermove 이벤트 타입을 위한 것입니다.
onpointerrawupdatepointerrawupdate 이벤트 타입을 위한 것입니다.
onpointeruppointerup 이벤트 타입을 위한 것입니다.
onpointercancelpointercancel 이벤트 타입을 위한 것입니다.
onpointeroutpointerout 이벤트 타입을 위한 것입니다.
onpointerleavepointerleave 이벤트 타입을 위한 것입니다.
ongotpointercapturegotpointercapture
이벤트 타입을 위한 것입니다.
onlostpointercapturelostpointercapture
이벤트 타입을 위한 것입니다.
Attributes and Default Actions에서 언급했듯이, 뷰포트 조작(패닝
및 줌)은 포인터 이벤트를 취소하여 억제할 수 없습니다. 대신 작성자는 touch-action CSS 속성을 사용해 허용할 동작과 억제할 동작을 선언적으로 정의해야
합니다.
touch-action CSS 속성 이름은 터치 입력만을 가리키는 것처럼 보이지만, 실제로는 패닝과 줌을 위한 direct manipulation을 허용하는 모든 형태의 포인터 입력에 적용됩니다.| Name: | touch-action |
|---|---|
| Value: | auto | none | [ [ pan-x | pan-left |
pan-right ] || [ pan-y | pan-up |
pan-down ] ] | manipulation
|
| Initial: | auto |
| Applies to: | 다음 제외 모든 요소: non-replaced inline 요소, table 행, 행 그룹, table 열, 열 그룹 |
| Inherited: | no |
| Percentages: | N/A |
| Media: | visual |
| Computed value: | 지정된 값과 동일 |
| Canonical order: | 문법 순서 |
| Animation type: | animatable 아님 |
touch-action CSS 속성은 이름과 달리 터치에만 국한되지 않는 direct
manipulation 상호작용이 user agent의 패닝 및 줌 동작을 트리거할 수 있는지 여부를 결정합니다. touch-action 값 섹션을 참고하세요.
패닝 또는 줌을 시작하기 직전에, user agent는 다음 모든 조건이 참이면 MUST로 포인터 이벤트 스트림을 억제해야 합니다:
pointerdown 이벤트가 전송되었으며,pointerdown 이후 아직 pointerup 또는 pointercancel 이벤트가 해당 포인터에 대해 전송되지 않았을 때.touch-action은 임베디드 브라우징 컨텍스트로 적용/상속되지 않습니다. 예를 들어
<iframe>에 touch-action을 적용해도 해당 <iframe> 내부의 패닝 및
줌 직접 조작 동작에는 영향이 없습니다.
사용자가 터치 또는 스타일러스 같은 direct manipulation 포인터로 요소를 상호작용할 때, 해당 입력의 효과는 다음과 같이
touch-action 속성 값과 요소 및 조상들의 기본 직접 조작 동작에 의해 결정됩니다:
touch-action에 적합(conform)합니다. CSS 변환이 적용된 경우 요소 좌표 공간이 화면 좌표와 달라져 적합성에 영향을 줄 수
있습니다(예: 화면 대비 90도 회전된 요소의 X축은 화면 Y축과 평행).touch-action에 적합하면 지원됩니다.
document 요소까지 모든 요소의 touch-action에 적합하면 지원됩니다.
touch-action 값 변경은
동작 지속 동안 무시됩니다. 예: pointerdown 핸들러 스크립트에서 요소의
touch-action을 auto에서 none으로 변경해도 그 포인터가 활성인 동안 진행 중인 패닝/줌을
중단하거나 억제하지 않습니다.
pan-* 값의 경우 제스처 시작 시 user agent가 제스처를 직접 처리할지 결정하면, 동일 제스처 방향 변화는 포인터가 활성인 동안
무시되어야 SHOULD 합니다. 예: 요소가 touch-action: pan-y(수직 패닝만 허용)일 때 터치
제스처가 수평으로 시작하면 사용자가 수직으로 방향을 바꾸더라도 손가락이 화면을 떠나기 전까지 수직 패닝은 발생하지 않아야 합니다.touch-action 값을 처리/연계하는 방법은 명세 범위를 벗어납니다.
touch-action 속성은 뷰포트 패닝 및 줌과 관련된 직접 조작 동작을 다룹니다. 텍스트 선택/강조, 링크 및 폼 컨트롤 활성화 같은 추가 user agent
동작은 이 CSS 속성에 의해 영향을 받아서는 MUST NOT 합니다.
auto, none 값에 대한 행동 정의는 명세 범위 밖입니다.pan-x 또는
pan-y) 패닝 중 축은 변경할 수 없습니다.
touch-action 값은 [COMPAT]에 정의되어 있습니다.touch-action 속성은 CSS width 및 height 속성을 모두 지원하는
요소에만 적용됩니다([CSS21]
참고). 이 제한은 저지연 direct manipulation 패닝 및 줌을 위한 user agent 최적화를 촉진하기
위해 설계되었습니다. 기본적으로 지원되지 않는 요소(예: non-replaced inline element인 <span>)는
작성자가 display를 block처럼 width와 height를 지원하는 값으로
설정하여 사용할 수 있습니다. 향후 명세는 이 API를 모든 요소로 확장할 수 있습니다.
방향별 pan 값은 일부 overscroll 동작을 커스터마이징하는 데 유용합니다.
예를 들어 단순한 pull-to-refresh 효과를 구현하려면 문서의
touch-action을 스크롤 위치가 0일 때 pan-x pan-down으로,
그렇지 않을 때 pan-x pan-y로 설정할 수 있습니다.
이렇게 하면 문서 최상단에서 시작하는 위쪽 패닝/스크롤에 대해 포인터 이벤트 핸들러가 동작을 정의할 수 있습니다.
방향별 pan 값은 네이티브로 스크롤되는 요소 내부에서 포인터 이벤트 처리로 커스텀 패닝을 구현하는 컴포넌트를 구성(또는 그 반대)하는 데도 사용할 수 있습니다.
예를 들어 이미지 캐러셀은 pan-y를 사용해 수평 패닝 작업에 대한 포인터 이벤트를 수신하면서 문서의 수직 패닝을 방해하지 않을 수 있습니다.
캐러셀이 가장 오른쪽 끝에 도달하면 이후 범위를 넘어선 스크롤 작업이 가능할 경우 뷰포트 내 문서를 스크롤할 수 있도록 touch-action을
pan-y pan-right로 변경할 수 있습니다.
진행 중인 패닝/스크롤 작업의 동작은 수행 중에 변경할 수 없습니다.
auto에서는 user agent가 더블 탭 제스처 처리를 허용하기 위해 일반적으로 click 전에 300ms 지연을 둡니다. 이러한
경우 touch-action: none 또는 touch-action: manipulation을 명시적으로 설정하면 이 지연이
제거됩니다. 탭/더블 탭 제스처 판별 방법은 명세 범위 밖입니다.
<div style="touch-action: none;">
이 요소는 패닝 또는 줌으로 이어질 수 있는 모든 직접 조작 상호작용에 대해 포인터 이벤트를 수신합니다.
</div>
<div style="touch-action: pan-x;">
이 요소는 수평 방향으로 패닝하지 않을 때 포인터 이벤트를 수신합니다.
</div>
<div style="overflow: auto;">
<div style="touch-action: none;">
이 요소는 패닝 또는 줌으로 이어질 수 있는 모든 직접 조작 상호작용에 대해 포인터 이벤트를 수신합니다.
</div>
<div>
이 요소에 대한 직접 조작 상호작용은 부모 조작에 사용될 수 </div>
</div>
</div>
<div style="overflow: auto;">
<div style="touch-action: pan-y;">
<div style="touch-action: pan-x;">
이 요소는 모든 직접 조작 상호작용에 대해 포인터 이벤트를 수신합니다.
수평 패닝만 허용하지만 그 사이의 중간 조상
(스크롤 가능한 요소 사이) 은 수직 패닝만 허용하기 때문입니다.
따라서 패닝/줌을 위한 직접 조작 동작은
user agent에 의해 처리되지 않습니다.
</div>
</div>
</div>
<div style="overflow: auto;">
<div style="touch-action: pan-y pan-left;">
<div style="touch-action: pan-x;">
이 요소는 왼쪽으로 패닝하지 않을 때 포인터 이벤트를 수신합니다.
</div>
</div>
</div>
이 섹션은 비규범적입니다.
포인터 캡처는 특정 포인터(및 모든 호환성 마우스 이벤트 포함)에 대한 이벤트를, 포인터 위치의 일반적인 히트 테스트
결과가 아닌 특정 요소로 재타겟팅할 수 있게 합니다. 이는 사용자 지정 슬라이더 컨트롤(예: [HTML]의
<input type="range"> 컨트롤과 유사) 같은 시나리오에서 유용합니다. 슬라이더 썸 요소에서 포인터 캡처를 설정하면, 포인터가 썸에서
벗어나더라도 사용자가 컨트롤을 앞뒤로 슬라이드할 수 있습니다.
pointerdown 후, 포인터 캡처를 사용하면 포인터가 썸에서 벗어나더라도
사용자가 썸을 슬라이드할 수 있습니다.포인터 캡처는 Element 타입의 element에 대해
element.setPointerCapture(pointerId) 메서드를 호출하여 설정합니다.
이 메서드가 호출되면, user agent는 다음
단계를 MUST로 수행해야 합니다:
pointerId가 어떤 활성 포인터와도 일치하지 않으면, "NotFoundError" DOMException을 throw 합니다.pointerId로 지정된 활성 포인터를 pointer로 둡니다.
InvalidStateError" DOMException을 throw 합니다.pointerLockElement)가
있는 경우,
"InvalidStateError" DOMException을 throw 합니다.
pointerId에 대해, pending pointer capture target override를 이 메서드가 호출된 Element로 설정합니다.pointerdown 리스너에서 해제하려다 실패한 경우에도 마찬가지입니다.포인터 캡처는 element.releasePointerCapture(pointerId) 메서드를 호출하여 요소에서 명시적으로 해제합니다. 이 메서드가 호출되면, user agent는 다음 단계를 MUST로 수행해야 합니다:
pointerId가 어떤 활성 포인터와도 일치하지 않고, 이 단계가 포인터 캡처의 암시적 해제의 결과로 호출된 것이 아니라면,
"NotFoundError" DOMException을 throw 합니다.pointerId에 대한 hasPointerCapture가 false이면,
이 단계를 종료합니다.
pointerId에 대해, 설정되어 있다면 pending pointer capture target
override를 제거합니다.패닝과 줌을 위한 직접 조작 상호작용(예: 터치스크린의 터치나 스타일러스)을 구현하는 입력은, 어떤 pointerdown 리스너가 호출되기 직전에 대상 요소에서 setPointerCapture가 호출된 것처럼 정확히 동작해야
SHOULD 합니다. 이 동작이 발생했는지 확인하기 위해(예: pointerdown 리스너에서), hasPointerCapture API를 사용할 수 있습니다. 다음
포인터 이벤트가 발생하기 전에 해당 포인터에 대해 releasePointerCapture가 호출되지 않으면,
캡처가 활성화되었음을 나타내는 gotpointercapture 이벤트가(일반적으로) 대상에 디스패치됩니다.
pointerup 또는 pointercancel 이벤트를 발생시킨 직후,
user agent는 해당 방금 디스패치된 pointerup
또는 pointercancel 이벤트의 pointerId에 대해 pending pointer capture target
override를 MUST로 지우고,
이어서 필요하면 lostpointercapture를 발생시키도록 process pending pointer capture 단계를 수행해야 합니다.
process pending pointer capture 단계를 수행한 후,
포인터가 hover를 지원한다면, user
agent는 캡처 없는 현재 포인터 위치를 반영하기 위해 필요한 경계(boundary) 이벤트도 MUST로 전송해야 합니다.
pointer capture target override가 더 이상 [DOM]의 connected 상태가 아니게 되면, pointer capture target override는 문서(document)로 설정되어야 SHOULD 합니다.
pending pointer capture target override가 더 이상 [DOM]의 connected 상태가 아니게 되면, pending pointer capture target override 노드는 SHOULD로 제거되어야 합니다.
lostpointercapture
이벤트가 발생하게 됩니다.
요소에 포인터 잠금 [PointerLock]이 성공적으로 적용되면, 어떤 요소가 캡처되었거나 캡처 대기 중인 경우,
user agent는 releasePointerCapture 메서드가 호출된 것처럼
단계를 MUST로 수행해야 합니다.
성능상의 이유로, user agent는 포인터의 측정 가능한 속성
(좌표, pressure, tangential pressure, tilt, twist, 접촉 기하 등)가 업데이트될 때마다 pointermove
이벤트를 보내지 않을 수 있습니다. 대신 여러 변경을 하나의 pointermove 또는 pointerrawupdate 이벤트로 병합(coalesce)할 수 있습니다.
이러한 방식은 user agent가 수행해야 하는
이벤트 처리 양을 줄이는 데 도움이 되지만,
특히 빠르고 큰 이동의 경우 포인터 위치를 추적할 때의 세분성(granularity)과 충실도(fidelity)가 자연스럽게 낮아집니다. getCoalescedEvents 메서드를 사용하면
애플리케이션이 병합되지 않은 원시 위치 변경에 접근할 수 있습니다. 이를 통해 포인터 이동 데이터를 보다 정밀하게 처리할 수 있습니다. 예를 들어 드로잉 애플리케이션에서는 병합되지 않은
이벤트를 사용해
실제 포인터 움직임에 더 가까운 매끄러운 곡선을 그릴 수 있습니다.
pointermove 이벤트에서 병합된
좌표(회색 점)만 사용하면
곡선이 눈에 띄게 각지고 거칩니다. 반면 getCoalescedEvents()가 제공하는 더 세밀한 점(빨간 원)을 사용해 그린 동일 선은
포인터 움직임을 더 부드럽게 근사합니다.PointerEvent는 연관된 coalesced
events list(0개 이상의 PointerEvent 목록)을 가집니다. 신뢰된(trusted) pointermove 및
pointerrawupdate 이벤트의 경우, 이 목록은 해당 이벤트로 병합된 모든
PointerEvent의 시퀀스입니다.
신뢰된 "부모" pointermove 및
pointerrawupdate 이벤트는 이러한 병합 이벤트의 누적을 나타내지만
(예: 디스플레이 주사율에 맞추는 등) 추가 처리를 포함할 수 있습니다.
결과적으로, 이러한 이벤트의 coalesced events list에는 항상 최소 하나의 이벤트가 포함됩니다.
그 외의 모든 신뢰된 이벤트 타입의 경우 목록은 비어 있습니다. 신뢰되지 않은(untrusted) 이벤트는 생성자에 전달된 값으로
coalesced events list가 초기화됩니다.
isTrusted 비트를
false로 설정하지만,
coalesced events list 내 이벤트들의 해당 비트는 원래의
true 값에서 변경되지 않습니다.
신뢰된 이벤트의 coalesced events list에 있는 이벤트는 다음을 가집니다:
timeStamp 값
[DOM] — 모든 병합 이벤트의
timeStamp는
getPredictedEvents 메서드를 호출한
디스패치된 포인터 이벤트의 timeStamp 이하입니다.
coalesced events list는 timeStamp 기준으로 시간순으로
정렬되어야 MUST 하며,
첫 번째 이벤트가 가장 작은 timeStamp를 가집니다.
pointerId,
pointerType,
isPrimary를 가집니다.
<style>
/* 고유의 user agent 직접 조작 동작(예: 패닝 또는 줌)을 비활성화하여
캔버스 요소의 모든 이벤트가 애플리케이션에 전달되도록 합니다. */
canvas { touch-action: none; }
</style>
<canvas id="drawSurface" width="500px" height="500px" style="border:1px solid black;"></canvas>
<script>
const canvas = document.getElementById("drawSurface"),
context = canvas.getContext("2d");
canvas.addEventListener("pointermove", (e)=> {
if (e.getCoalescedEvents) {
for (let coalesced_event of e.getCoalescedEvents()) {
paint(coalesced_event); // 원시/비병합 포인트 모두 그리기
}
} else {
paint(e); // 최종 병합 포인트 그리기
}
});
function paint(event) {
if (event.buttons>0) {
context.fillRect(event.clientX, event.clientY, 5, 5);
}
}
</script>
디스패치된 모든 이벤트의 순서는 원본 이벤트의 실제 순서와 MUST로 일치해야 합니다.
예를 들어 pointerdown 이벤트가 병합된 pointermove 이벤트들의 디스패치를 유발하는 경우, user agent는 먼저 해당 pointerId의 모든 병합 이벤트를 포함하는 하나의 pointermove 이벤트를 디스패치하고, 그 다음에 pointerdown 이벤트를 디스패치해야 합니다.
다음은 증가하는 timeStamp 값을 가진 실제
이벤트와
user agent가 디스패치한 이벤트의 예시입니다:
| 실제 이벤트 | 디스패치된 이벤트 |
|---|---|
포인터 (pointerId=2)
좌표 변경 |
pointerrawupdate (pointerId=2), 병합 이벤트
1개 포함 |
포인터 (pointerId=1)
좌표 변경 |
pointerrawupdate (pointerId=1), 병합 이벤트
1개 포함 |
포인터 (pointerId=2)
좌표 변경 |
pointerrawupdate (pointerId=2), 병합 이벤트
1개 포함 |
포인터 (pointerId=2)
좌표 변경 |
pointerrawupdate (pointerId=2), 병합 이벤트
1개 포함 |
포인터 (pointerId=1)
좌표 변경 |
pointerrawupdate (pointerId=1), 병합 이벤트
1개 포함 |
포인터 (pointerId=2)
좌표 변경 |
pointerrawupdate (pointerId=2), 병합 이벤트
1개 포함 |
포인터 (pointerId=1) 버튼
누름 |
pointermove (pointerId=1), 병합 이벤트
2개 포함pointermove (pointerId=2), 병합 이벤트
4개 포함pointerdown (pointerId=1), 병합 이벤트
0개 |
포인터 (pointerId=2)
좌표 변경 |
pointerrawupdate (pointerId=2), 병합 이벤트
1개 포함 |
포인터 (pointerId=2)
좌표 변경 |
pointerrawupdate (pointerId=2), 병합 이벤트
1개 포함 |
포인터 (pointerId=1) 버튼
뗌 |
pointermove (pointerId=2), 병합 이벤트
2개 포함pointerup (pointerId=1), 병합 이벤트
0개 |
일부 user agent는 일련의 확인된 포인터 이동 이후, 현재 제스처에 대한 선행 이벤트 및 이동의 속도/궤적을 바탕으로
향후 포인터 이동의 위치를 예측할 수 있는 내장 알고리즘을 갖고 있습니다. 애플리케이션은 이 정보를 getPredictedEvents 메서드와 함께 사용해
체감 지연을 줄이기 위해 추정 위치로 미리 “앞서 그리기”를 수행하고, 실제 포인트가 수신되면 이 예측 포인트를 폐기할 수 있습니다.
pointermove 이벤트의 병합 좌표를 사용하며,
user agent가 예측한 미래 포인트(회색 원)를 표시합니다.PointerEvent는 연관된 predicted
events list(0개 이상의 PointerEvent 목록)을 가집니다. 신뢰된 pointermove 이벤트의 경우,
user agent가 향후 해당 이벤트를 따라올 것으로 예측하는 PointerEvent들의 시퀀스입니다.
그 외의 모든 신뢰된 이벤트 타입의 경우 목록은 비어 있습니다.
신뢰되지 않은 이벤트는 생성자에 전달된 값으로 predicted events list가 초기화됩니다.
pointerrawupdate 이벤트는 비어 있지 않은 coalesced
events list를 가질 수 있지만,
성능상의 이유로 predicted events list는 보통 비어 있습니다.
isTrusted 비트를
false로 설정하지만,
predicted events list 내 이벤트들의 해당 비트는 원래의
true 값에서 변경되지 않습니다.
목록의 이벤트 개수와 현재 타임스탬프로부터의 거리(미래 시간)는 user agent와 그가 사용하는 예측 알고리즘에 의해 결정됩니다.
신뢰된 이벤트의 predicted events list에 있는 이벤트는 다음을 가집니다:
timeStamp 값
[DOM] — 모든 예측 이벤트의
timeStamp는
getPredictedEvents 메서드를 호출한
디스패치된 포인터 이벤트의 timeStamp 이상입니다.
predicted events list는 timeStamp 기준으로 시간순으로
정렬되어야 MUST 하며,
첫 번째 이벤트가 가장 작은 timeStamp를 가집니다.
pointerId,
pointerType,
isPrimary를 가집니다.
작성자는 예측 이벤트를 다음 포인터 이벤트가 디스패치될 때까지의 유효한 예측으로만 간주해야 합니다. user agent가 어느 정도 미래까지 이벤트를 예측하는지에 따라, 일반 포인터 이벤트가 하나 이상의 예측 이벤트의 타임스탬프보다 먼저 디스패치될 수 있습니다.
let predicted_points = [];
window.addEventListener("pointermove", function(event) {
// 이전에 그려진 예측 포인트를 지웁니다.
for (let e of predicted_points.reverse()) {
clearPoint(e.pageX, e.pageY);
}
// 마지막으로 받은 이벤트 이후 실제로 발생한 움직임을 그립니다.
for (let e of event.getCoalescedEvents()) {
drawPoint(e.pageX, e.pageY);
}
// 지연 체감을 줄이기 위해 현재 예측 포인트를 그립니다.
predicted_points = event.getPredictedEvents();
for (let e of predicted_points) {
drawPoint(e.pageX, e.pageY);
}
});
신뢰된 PointerEvent가 생성될 때, user agent는
coalesced events list와 predicted events list의 각 이벤트에 대해 다음 단계를 SHOULD로 수행해야 합니다:
pointerId,
pointerType,
isPrimary, isTrusted를
"부모" 포인터 이벤트의 해당 속성과 일치하도록 설정합니다.
cancelable 및 bubbles를 false로 설정합니다
(이 이벤트들은 단독으로 디스패치되지 않기 때문입니다).PointerEvent 값으로 초기화합니다.
신뢰된 PointerEvent의 target이 변경되면, user agent는
coalesced events list와 predicted events list의 각 이벤트에 대해 다음을 SHOULD로 수행합니다:
오늘날 존재하는 웹 콘텐츠의 절대 다수는 오직 Mouse Events에 맞추어 작성되어 있습니다. 다음은 user agent가 일반적인 포인터 입력을 이 콘텐츠와의 호환성을 위해 마우스 이벤트로 MAY 매핑하는 알고리즘을 설명합니다.
마우스 이벤트와의 호환성 매핑은 이 명세의 OPTIONAL 기능입니다. User agent는 기존 레거시 콘텐츠와 최상의 호환성을 위해 이 기능을 지원하는 것이 권장됩니다.
높은 수준에서, 호환성 마우스 이벤트는 해당하는 포인터 이벤트와 “인터리브”되도록 의도되었습니다. 그러나 이 구체적 순서는 필수는 아니며, 호환성 마우스 이벤트를 구현하는 user agent는 상대적 순서가 일관되는 한 마우스 이벤트 디스패치를 지연하거나 묶어서 MAY 할 수 있습니다.
특히 터치스크린 입력의 경우, user agent는 (작성자가 으로 명시적으로 억제하지 않는 한)
제스처 인식을 위한 추가 휴리스틱을 MAY 적용할 수 있습니다. touch-actionpointerdown 이벤트와 pointerup 이벤트 사이의 이벤트 시퀀스 동안 제스처 인식은 어떤 제스처를 감지할지
/ 무시할지를 판단하기 위해 pointerup 이벤트까지 대기해야 할 수도 있습니다. 그 결과 user agent가
특정 제스처로 의도되지 않았다고 판단하면 전체 시퀀스에 대한 호환성 마우스 이벤트가 마지막 pointerup 이후 함께 디스패치될 수 있습니다.
이러한 user agent 제스처 인식의 구체사항은 이 명세에서 정의되지 않으며 구현마다 다를 수 있습니다.
호환성 마우스 이벤트 지원 여부와 관계없이, user agent는 MUST 항상 click,
auxclick, contextmenu 이벤트를 지원해야 합니다. 이는 해당 이벤트가 PointerEvent 타입이며 따라서
호환성 마우스 이벤트가 아니기 때문입니다. 포인터 이벤트 동안
preventDefault를 호출하는 것은 MUST NOT click,
auxclick, contextmenu 발생 여부에 영향을 주지 않습니다.
일부 고수준 이벤트(contextmenu, focus, blur 등)의 포인터 이벤트와의 상대적
순서는 정의되지 않았으며 user agent마다 다릅니다. 예: 일부 user agent에서는 contextmenu가 종종 pointerup 뒤에 오지만, 다른 것에서는 pointerup 또는 pointercancel 앞에 오기도 하며, 어떤 상황에서는 (예: 키보드
상호작용 결과) 해당하는 포인터 이벤트 없이 발생할 수도 있습니다.
추가로 user agent는 click, auxclick, contextmenu 이벤트를 발생시킬지 자체
휴리스틱을 적용할 수 있습니다. 일부 user agent는 같은 타입의 다른 (primary가 아닌) 포인터가 있거나, 다른 타입의 primary 포인터가 있는 경우 이러한
이벤트를 발생시키지 않을 수 있습니다. 또한 user agent는 특정 동작이 “깨끗한” 탭, 클릭, 롱프레스가 아니었다고(예: 터치 스크린에서 손가락이 접촉 중 과도하게 움직임)
판단하여 click, auxclick, contextmenu 이벤트를 발생시키지 않기로 결정할 수 있습니다.
이러한 user agent 동작 측면은 명세에서 정의하지 않으며 구현마다 다를 수 있습니다.
별도 언급이 없는 한, 매핑된 마우스 이벤트의 대상은 해당 포인터 이벤트의 대상과 동일해야 SHOULD 하며, 대상이 더 이상 자신의
ownerDocument 트리에 참여하지 않는 경우 마우스 이벤트는 (트리에서 제거된 시점의) 원래 대상의 여전히 트리에 참여하는 가장 가까운 상위 노드에서 발생해야
합니다. 즉, 마우스 이벤트를 위해 (새 대상 노드를 기반으로) 새로운 이벤트 경로가 구성됩니다.
작성자는 pointerdown 이벤트를 취소하여 특정 호환성 마우스 이벤트 생성을 방지할 수 있습니다.
마우스 이벤트는 포인터가 down 상태일 때만 방지될 수 있습니다. Hover 상태의 포인터(예: 버튼을 누르지 않은 마우스)는 그 마우스 이벤트를 방지할 수 없습니다.
mouseover, mouseout, mouseenter, mouseleave 이벤트는 (포인터가
down 상태여도) 결코 방지되지 않습니다.
호환성 마우스 이벤트는 포인터 이벤트 EventListener가 [DOM]의 passive로 설정되어
있을 때는 방지할 수 없습니다.
primary pointer만 호환성 마우스 이벤트를 생성할 수 있지만, 복수의 primary 포인터가 동시에 활성 상태일 수 있으며 각 포인터는 자체 호환성 마우스 이벤트를
생성합니다. MouseEvent에 의존하는 스크립트와의 호환성을 위해 마우스 전환 이벤트(mouseover, mouseout,
mouseenter, mouseleave)는 단일 레거시 마우스 입력의 이동을 시뮬레이션해야 SHOULD 합니다. 이는 모든 이벤트 타겟의 진입/이탈 상태가 [UIEVENTS]에 부합하도록 하기
위함입니다. User agent는 문서에서 레거시 마우스 포인터의 유효 위치를 다음과 같이 유지하여 이를 보장해야 SHOULD
합니다.
pointerdown, pointerup,
pointermove 이벤트 또는 window에서 발생하는 pointerleave 이벤트를 발행하기 직전에, user agent는 다음 단계를 SHOULD 수행해야 합니다:
pointerdown, pointerup 또는 pointermove 이벤트의 대상으로 둡니다. pointerleave 이벤트의 경우 T를 unset 합니다.
mouseover, mouseout,
mouseenter, mouseleave를 디스패치합니다. 현재 유효 위치 또는 T가 unset이면 창
밖(out-of-window) 마우스 위치로 간주합니다.
레거시 마우스 포인터의 유효 위치 개념은 포인터 전환
이벤트(pointerover, pointerout, pointerenter,
pointerleave)와 해당하는 레거시 마우스 전환 이벤트(mouseover, mouseout,
mouseenter, mouseleave) 사이에 항상 직접 매핑이 존재하지 않는 사실을 모델링합니다. 아래 애니메이션은 단일
레거시 마우스 입력을 사용해 두 개의 primary 포인터를 조화시키기 위해 user agent가 포인터 전환 이벤트보다 더 많은 레거시 마우스 전환 이벤트를 디스패치해야
하는 사례를 보여줍니다.
이 애니메이션에서, 마우스 클릭과 터치 탭 사이 기간을 주목하세요. 버튼 1은 (이 기간 동안 “실제” 마우스 포인터가 버튼 사각형을 벗어나지 않았기 때문에)
pointerout 이벤트를 받지 않지만, 터치 탭 시 레거시 마우스 포인터의 유효 위치가 버튼 2로 이동할
때 mouseout 이벤트를 받습니다. 유사하게 터치 탭과 마우스가 버튼 1을 떠나기 전 사이 기간에도 같은 이유로 버튼 1은
pointerover 이벤트를 받지 않지만, 유효 위치가 다시 버튼 1 내부로 돌아올 때
mouseover 이벤트를 받습니다.
User agent가 hover를 지원하는 장치에 대해 포인터 이벤트를 디스패치하려 할 때마다, 다음 단계를 SHOULD 수행해야 합니다:
isPrimary 속성이 false이면 포인터 이벤트를 디스패치하고 종료합니다.pointerdown, pointerup, pointermove 이벤트이거나, window에서의 pointerleave 이벤트라면 레거시 마우스 포인터의 유효
위치 추적에 설명된 대로 호환성 마우스 전환 이벤트를 디스패치합니다.pointerdown이고 그 이벤트가 canceled 되었다면, 이 pointerType에 대해
PREVENT MOUSE EVENT 플래그를 설정합니다.
PREVENT MOUSE EVENT 플래그가 해당 pointerType에 설정되어 있지 않고 디스패치된 포인터 이벤트가 다음 중
하나라면:
pointerdown → mousedown
이벤트를 발생pointermove → mousemove
이벤트를 발생pointerup → mouseup 이벤트를 발생
pointercancel → window에서
mouseup 이벤트를 발생
pointerup 또는 pointercancel였다면, 해당
pointerType의 PREVENT MOUSE EVENT 플래그를 해제합니다.
대부분의 터치스크린과 같은 일부 장치는 활성 상태가 아닐 때 좌표(또는 좌표 집합)를 hover하는 기능을 지원하지 않습니다. 마우스 이벤트에 맞추어 작성된 기존 콘텐츠는 마우스가 이벤트를 생성한다고 가정하므로 일반적으로 다음 특성이 참입니다:
mousemove 이벤트를 생성할 가능성이 높다.이러한 유형의 입력 장치에 대해 user agent는 다른 매핑을 제공해야 합니다. User agent가 hover를 지원하지 않는 장치에 대해 포인터 이벤트를 디스패치하려 할 때마다 다음 단계를 SHOULD 수행해야 합니다:
isPrimary 속성이 false이면 포인터 이벤트를 디스패치하고 종료합니다.pointerover이고 해당 포인터에 대한 pointerdown 이벤트가 아직 디스패치되지 않았다면 레거시 마우스 전용 코드를
위한 호환성으로 mousemove 이벤트를 발생시킵니다.pointerdown, pointerup, pointermove 이벤트이거나, window에서의 pointerleave 이벤트라면 레거시 마우스 포인터의 유효
위치 추적에 설명된 대로 호환성 마우스 전환 이벤트를 디스패치합니다.pointerdown이고 그 이벤트가 canceled 되었다면 해당 pointerType에
PREVENT MOUSE EVENT 플래그를 설정합니다.
PREVENT MOUSE EVENT 플래그가 설정되지 않았고 디스패치된 포인터 이벤트가 다음 중 하나라면:
pointerdown → mousedown
이벤트 발생pointermove → mousemove
이벤트 발생pointerup → mouseup 이벤트 발생
pointercancel → window에서
mouseup 이벤트 발생
pointerup 또는 pointercancel라면 해당 pointerType의
PREVENT MOUSE EVENT 플래그를 해제합니다.
User agent가 [TOUCH-EVENTS]에 정의된 Touch Events와 Pointer Events를 모두 지원하는 경우, user agent는 이 섹션에 기술된 호환성 마우스 이벤트와 [TOUCH-EVENTS]에 개괄된 폴백 마우스 이벤트를 MUST NOT 둘 다 생성해서는 안 됩니다.
hover를 지원하지 않는 primary 포인터(예: 터치스크린의
단일 손가락)로 요소를 활성화(click) 하면 일반적으로 다음 이벤트 시퀀스를 생성합니다:
mousemovepointeroverpointerentermouseovermouseenterpointerdownmousedownpointermove 및 mousemove
이벤트pointerupmouseuppointeroutpointerleavemouseoutmouseleaveclick그러나 이 상호작용 중 pointerdown 이벤트가 canceled 된다면 시퀀스는 다음과 같습니다:
mousemovepointeroverpointerentermouseovermouseenterpointerdownpointermove 이벤트pointeruppointeroutpointerleavemouseoutmouseleaveclick이 부록은 Pointer Events 구현에 대한 보안 및 프라이버시 고려사항을 논의합니다. 논의는 이 명세에서 정의한 이벤트 모델, API, 이벤트 구현에서 직접 발생하는 보안 및 프라이버시 이슈로 한정됩니다.
이 명세에서 정의된 많은 이벤트 타입은 사용자 동작에 대한 응답으로 디스패치됩니다. 이는 악의적인 이벤트 리스너가 사용자가 일반적으로 기밀로 간주하는 정보(예: 페이지 상호작용 중 사용자의 마우스/스타일러스/손가락 이동 경로)를 획득할 수 있게 합니다.
포인터 이벤트에는 (사용자 장치가 지원하는 경우) 펜 입력이 유지되는 각도나 기울기, 접촉 표면의 기하, 스타일러스 또는 터치 스크린에 가해진 압력과 같은 추가 정보가 포함됩니다. 각도, 기울기, 기하 및 압력 정보는 사용자 장치의 센서와 직접 관련되므로 이 명세는 출처(origin)가 이러한 센서에 접근하는 것을 허용합니다.
이 센서 데이터 및 사용된 입력 메커니즘 유형(마우스, 터치, 펜)을 판별하는 능력은 사용자 또는 사용자 장치 및 환경 특성을 추론하는 데 사용될 수 있습니다. 이러한 추론된 특성과 장치/환경 정보 자체는 민감할 수 있으며—예: 악의적인 사이트가 사용자가 보조기술을 사용 중인지 추가로 추론할 수 있도록 할 수 있습니다. 이 정보는 사용자 프로파일 작성 및/또는 특정 사용자를 “지문채취(fingerprinting)”하여 추적하는 데 잠재적으로 사용될 수 있습니다.
완화책으로, user agent는 특정 센서 데이터(각도, 기울기, 압력 등)에 대한 접근을 사용자가 비활성화하거나, 사용자 명시적 옵트인(opt-in) 이후에만 이용 가능하도록 하는 기능을 포함하는 것을 고려할 수 있습니다.
이 명세는 작성자가 “predicted events”에 접근하는 방법을 정의합니다. 자체적으로 user agent가 예측에 사용할 알고리즘은 정의하지 않습니다. 명세 작성자들은 알고리즘이 사용자가 수행 중인 현재 제스처와 관련된 이전 포인터 이벤트에만 의존한다고 예상합니다. User agent는 자체 예측 알고리즘 구현이 사용자나 여러 사이트에 걸친 전체 상호작용 기록 등 추가 데이터에 의존하지 않아 사용자에 대한 민감한 정보를 노출하거나 “fingerprint”에 사용되지 않도록 보장할 책임이 있습니다.
이러한 고려사항 외에, 작업 그룹은 이 명세가 다음과 같다고 판단합니다:
이 섹션은 비규범적입니다.
buttons 속성 값이 0이 아닌 상태. 마우스의 경우 하나 이상의 버튼이 눌려 있을 때. 터치의 경우 디지타이저와 물리적 접촉이 있을 때. 펜의 경우 펜이
디지타이저와 물리적 접촉 중이거나, 호버 중 하나 이상의 버튼이 눌린 경우.pointerId로 식별되는 포인터가 문서 내에서 추가 이벤트를 생성할
가능성이 있으면 여전히 active로 간주. 예:
preventDefault() 호출, 이벤트 핸들러에서 false 반환, 또는 [UIEVENTS] 및
[HTML]에 정의된 기타 수단으로 기본 동작이 방지된 이벤트.Measurable properties는 연속적인 포인터 센서 데이터와 관련된 값으로, 실수 또는 큰 도메인의 정수로 표현됩니다. 포인터 이벤트에서
width, height, pressure,
tangentialPressure, tiltX, tiltY, twist,
altitudeAngle, azimuthAngle 및 [UIEVENTS] 마우스 이벤트
모델 속성 screenX, screenY, clientX, clientY는
measurable properties입니다.
반면 pointerId, pointerType,
isPrimary, 및 [UIEVENTS] 마우스 이벤트 모델 속성 button,
buttons, ctrlKey,
shiftKey, altKey, metaKey는 센서 데이터와 관련되지 않으므로 measurable
properties로 간주되지 않습니다.
많은 분들의 제안과 권고에 깊이 감사드리며, 그중 일부는 본 문서에 반영되었습니다. 그룹 의장은 다음의 과거 및 현재 그룹 구성원과 참가자들의 기여를 인정합니다: Mustaq Ahmed, Arthur Barstow, Ben Boyle, Matt Brubeck, Rick Byers, Marcos Cáceres, Cathy Chan, Bo Cupp, Domenic Denicola, Ted Dinklocker, Adam Ettenberger, Robert Flack, Dave Fleck, Mike Fraser, Ella Ge, Olga Gerchikov, Scott González, Kartikaya Gupta, Dominique Hazael-Massieux, Philippe Le Hégaret, Hayato Ito, Patrick Kettner, Patrick H. Lauke, Scott Low, Sangwhan Moon, Masayuki Nakano, Olli Pettay, Addison Phillips, Alan Pyne, Antoine Quint, Jacob Rossi, Kagami Sascha Rosylight, Doug Schepers, Ming-Chou Shih, Brenton Simpson, Dave Tapuska, Liviu Tinta, Asir Vedamuthu, Lan Wei, Jeffrey Yasskin, Navid Zolghadr.
이 모델의 초판을 개척하는 데 도움을 주신 분들께 특별히 감사드립니다. 특히: Charu Chandiram, Peter Freiling, Nathan Furtwangler, Thomas Olsen, Matt Rakow, Ramu Ramanathan, Justin Rogers, Jacob Rossi, Reed Townsend 및 Steve Wright.
이 섹션은 비규범적입니다.
다음은 [PointerEvents3] 명세와 비교하여, 이 명세의 각 발행본 간에 이루어진 중대한 변경 및 주요 편집 변경 사항에 대한 참고용 요약입니다. 이 명세의 편집자 초안 전체 개정 이력을 참조하십시오.
WebIDLdictionary PointerEventInit : MouseEventInit {
long pointerId = 0;
double width = 1;
double height = 1;
float pressure = 0;
float tangentialPressure = 0;
long tiltX;
long tiltY;
long twist = 0;
double altitudeAngle;
double azimuthAngle;
DOMString pointerType = "";
boolean isPrimary = false;
long persistentDeviceId = 0;
sequence<PointerEvent> coalescedEvents = [];
sequence<PointerEvent> predictedEvents = [];
};
[Exposed=Window]
interface PointerEvent : MouseEvent {
constructor(DOMString type, optional PointerEventInit eventInitDict = {});
readonly attribute long pointerId;
readonly attribute double width;
readonly attribute double height;
readonly attribute float pressure;
readonly attribute float tangentialPressure;
readonly attribute long tiltX;
readonly attribute long tiltY;
readonly attribute long twist;
readonly attribute double altitudeAngle;
readonly attribute double azimuthAngle;
readonly attribute DOMString pointerType;
readonly attribute boolean isPrimary;
readonly attribute long persistentDeviceId;
[SecureContext] sequence<PointerEvent> getCoalescedEvents();
sequence<PointerEvent> getPredictedEvents();
};
partial interface Element {
undefined setPointerCapture (long pointerId);
undefined releasePointerCapture (long pointerId);
boolean hasPointerCapture (long pointerId);
};
partial interface mixin GlobalEventHandlers {
attribute EventHandler onpointerover;
attribute EventHandler onpointerenter;
attribute EventHandler onpointerdown;
attribute EventHandler onpointermove;
[SecureContext] attribute EventHandler onpointerrawupdate;
attribute EventHandler onpointerup;
attribute EventHandler onpointercancel;
attribute EventHandler onpointerout;
attribute EventHandler onpointerleave;
attribute EventHandler ongotpointercapture;
attribute EventHandler onlostpointercapture;
};
partial interface Navigator {
readonly attribute long maxTouchPoints;
};
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in: