指针事件

第3级

W3C 候选推荐快照

关于本文件的更多详情
本版本:
https://www.w3.org/TR/2025/CR-pointerevents3-20251106/
最新已发布版本:
https://www.w3.org/TR/pointerevents3/
最新编辑草稿:
https://w3c.github.io/pointerevents/
历史记录:
https://www.w3.org/standards/history/pointerevents3/
提交历史
测试套件:
https://wpt.fyi/pointerevents/
实现报告:
https://wpt.fyi/pointerevents/
最新推荐标准:
https://www.w3.org/TR/pointerevents2
编辑:
Patrick H. Lauke (TetraLogical)
Robert Flack (Google)
前任编辑:
Matt Brubeck (Mozilla)
Rick Byers (Google)
Navid Zolghadr (Google)
反馈:
GitHub w3c/pointerevents (拉取请求, 新建议题, 开放议题)
public-pointer-events@w3.org 邮件主题为 [pointerevents3] … 消息主题 … (邮件归档)
浏览器支持:
caniuse.com

摘要

本规范中的功能扩展或修改了《Pointer Events》(指针事件)中的内容。Pointer Events 是一份由W3C(万维网联盟)推荐标准,描述了用于处理来自包括鼠标、手写笔或触摸屏等设备的与硬件无关的指针输入的事件及相关接口。为了兼容现有基于鼠标的内容,本规范还描述了为其他指针设备类型触发鼠标事件的映射。

本文档现状

本节描述了本文档在发布时的状态。当前W3C 出版物列表及本技术报告的最新修订版可在 W3C 标准和草案索引中找到。

本规范是对[PointerEvents2]的更新。

本次修订包含了新特性:

本次修订还包括如下澄清:

在本规范退出候选推荐阶段前,至少需有两个或更多独立实现通过每一项测试,尽管不要求每个实现都通过所有测试。

本文档由 Pointer Events 工作组 作为候选推荐快照发布, 并采用推荐流程

作为候选推荐发布,不代表W3C及其成员的认可。候选推荐快照已接受广泛评审, 旨在收集实现经验,且工作组成员已承诺为实现提供 免版税授权

本候选推荐计划最早于 2025年12月4日进入提议推荐阶段。

本文档由遵循 W3C 专利政策的工作组制作。 W3C维护了一份 与本组成果相关的任何专利披露的公开列表; 该页面还包含专利披露说明。任何个人若知晓某专利并认为其包含 必要声明, 必须按照 W3C专利政策第6节进行披露。

本文档受 2025年8月18日 W3C流程文档的管理。

1. 简介

本节为非规范性内容。

如今,大多数[HTML]内容都与鼠标输入相关,或者为鼠标输入所设计。那些以自定义方式处理输入的内容,通常会以[UIEVENTS]鼠标事件为基础进行编码。然而,现代计算设备除了鼠标,还集成了其他输入方式,例如触摸屏和手写笔。针对每一种输入方式,已经提出了相应的事件类型用于逐一处理。然而,当需要添加对新输入类型的支持时,这种方式往往会造成逻辑和事件处理的冗余,带来不必要的开销。内容如果只考虑了一种设备类型,也会造成兼容性问题。另外,为了兼容现有基于鼠标的内容,大多数用户代理会为所有输入类型触发鼠标事件。这使得 MouseEvent 难以区分是由实际鼠标设备产生,还是为兼容性由其它输入类型生成,导致难以同时兼容代码处理不同设备类型。

为了减少多输入类型编码的复杂度,并解决上述 MouseEvent 的歧义问题,本规范定义了一种更抽象的输入形式,称为指针。指针可以是由鼠标光标、手写笔、触摸(包括多点触控)或其他指向型输入设备在屏幕上产生的任意接触点。 这种模型可以让网站和应用无论用户使用哪种硬件,都能良好工作。如果需要针对具体设备进行处理,本规范还定义了检查产生事件的设备类型的属性。主要目标是提供一组统一的事件和接口,让开发者能更轻松地进行跨设备指针输入编程,仅在确需增强体验时再进行设备专用的处理。

另一个关键目标是,使多线程用户代理可以响应直接操作动作,如平移和缩放(比如在触摸屏上用手指或手写笔),且不会因脚本执行而阻塞。

注意

虽然本规范定义了适用于各种指针输入的统一事件模型,但该模型不涵盖键盘或类似键盘的接口(例如,在仅有触摸屏的设备上运行的屏幕阅读器或类似辅助技术,允许用户通过聚焦控件顺序导航)。虽然用户代理也可能选择针对这些接口生成指针事件,但此类场景并不包含在本规范之中。

首先建议作者通过响应高级事件(如focusblurclick)为所有输入方式提供等效功能。但在使用底层事件(如Pointer Events)时,建议作者确保支持所有输入类型。对于键盘及类似接口,可能需要显式添加键盘事件处理。更多详情可参考无障碍键盘操作 [WCAG22]。

指针输入结合了鼠标、手写笔和触摸等多种输入源
1 指针是一种与硬件无关的输入设备抽象,可以定位到屏幕上的具体坐标(或一组坐标)。

用于处理通用指针输入的事件类似于鼠标事件:pointerdownpointermovepointeruppointeroverpointerout 等。这样有助于内容从 Mouse Events 平滑迁移到 Pointer Events。 Pointer Events 在包含 Mouse Events 所有常用属性(如客户端坐标、目标元素、按键状态)的基础上,还针对压力、接触几何、倾斜等输入形式提供了新属性。作者可以方便地以 Pointer Events 复用不同输入类型的处理逻辑,仅在需要时为某种输入类型定制特殊体验。

指针事件虽然来源于多种输入设备,但其本身不是由其他设备专有事件生成的定义。虽然允许并推荐为兼容性目的这样做,但本规范不要求支持其他设备专有事件(如鼠标事件或触摸事件)。用户代理可以只支持指针事件而完全不支持其它事件。为兼容那些基于鼠标事件开发的内容,本规范还增加了可选章节,描述如何基于鼠标以外的指针输入生成兼容性鼠标事件

注意

本规范未对同时支持 Touch Events(定义见[TOUCH-EVENTS])和 Pointer Events 的用户代理所应有的行为提供建议。 关于这两类规范之间的关系,可以参见 Touch Events 社区组

2. 一致性

除标注为非规范性的部分外,本规范中的全部作者指南、图示、示例和注释均属于非规范性内容。规范性内容为本规范中的其他内容。

本文档中的关键词MAYMUSTMUST NOTOPTIONALSHOULD,应按照 BCP 14 [RFC2119] [RFC8174] 的说明进行解释,且仅当这些词以本文所示全部大写方式出现时如此。

3. 示例

本节为非规范性内容。

以下是一些基础示例,展示了本规范中部分 API 可如何被作者使用。更多、更具体的示例请见本文相关章节。

示例 1:功能检测与事件绑定
/* 绑定 Pointer Events 或传统触摸/鼠标事件 */

if (window.PointerEvent) {
    // 支持 Pointer Events 时,仅监听指针事件
    target.addEventListener("pointerdown", function(e) {
        // 如有必要,可根据 e.pointerType 分别处理手触/手写笔/鼠标逻辑
        // 以适配不同行为
        ...
    });
    ...
} else {
    // 传统触摸/鼠标事件处理函数
    target.addEventListener('touchstart', function(e) {
        // 阻止兼容性鼠标事件和点击事件
        e.preventDefault();
        ...
    });
    ...
    target.addEventListener('mousedown', ...);
    ...
}

// 为键盘处理绑定其他事件监听器
...
示例 2:检测用户输入类型
window.addEventListener("pointerdown", detectInputType);

function detectInputType(event) {
    switch(event.pointerType) {
        case "mouse":
            /* 检测到鼠标输入 */
            break;
        case "pen":
            /* 检测到手写笔/触控笔输入 */
            break;
        case "touch":
            /* 检测到触摸输入 */
            break;
        default:
            /* pointerType 为空(无法检测到),或 UA 定义的自定义类型 */
    }
}
示例 3:根据接触几何调整元素尺寸
<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>
示例 4:脚本触发不受信任的指针事件
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);

4. 指针事件及接口

4.1 PointerEvent 接口

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;
    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;
    [SecureContext] sequence<PointerEvent> getCoalescedEvents();
    sequence<PointerEvent> getPredictedEvents();
};
pointerId

导致事件的指针的唯一标识符。用户代理可以 (MAY) 为主鼠标指针保留一个通用的 pointerId01。值 -1 必须 (MUST) 保留并用于指示由非指向设备生成的事件。对于其他指针,用户代理可自由采用不同策略和方法分配 pointerId 值。但在活动指针所属的顶级浏览上下文(参见 [HTML])中,所有标识符必须唯一,且该标识符不得 (MUST NOT)受其它顶级浏览上下文影响(即某顶级上下文不能假设指针移出其上下文进入另一上下文时 pointerId 会保持不变)。

用户代理可以 (MAY)回收之前已退休的活动指针的值,或可以 (MAY)总是为特定指针设备重用同一个 pointerId (例如在多用户协作应用中唯一识别特定用户的某支手写笔)。然而在后一种情形中,为最小化跨页面或域的指纹与跟踪风险,该 pointerId必须 (MUST)仅在该页面/会话生命周期内与该特定设备显式关联;在新会话再次使用该设备时必须 (MUST)选择一个新的随机化 pointerId

注意

pointerId 选取算法依赖实现。作者不能假设这些值除唯一性外还有其它含义。例如,用户代理可能仅按激活顺序从 0 开始依次分配数字,但这些值不保证单调递增。由于是否为同一设备重用同一 pointerId 取决各实现,强烈不建议作者依赖此行为。

width

指针的接触几何在 X 轴的宽度(CSS 像素,参见 [CSS21])。该值对给定指针的每个事件可以 (MAY)更新。对于通常缺乏接触几何的输入(如传统鼠标),以及硬件未检测到实际几何的情况,用户代理必须 (MUST)返回默认值 1

height

指针的接触几何在 Y 轴的高度(CSS 像素,参见 [CSS21])。该值对给定指针的每个事件可以 (MAY)更新。对于通常缺乏接触几何的输入(如传统鼠标),以及硬件未检测到实际几何的情况,用户代理必须 (MUST)返回默认值 1

pressure

指针输入的归一化压力,范围 [0,1],其中 01 分别表示硬件可检测的最小与最大压力。对不支持压力的硬件或平台,当处于活动按钮状态时该值必须 (MUST)0.5,否则为 0

tangentialPressure

归一化切向压力(亦称笔筒压力),通常由额外控制(例如喷笔触控笔上的指轮)设定,范围 [-1,1]0 为控制的中性位置。部分硬件可能只支持正区间 [0,1]。不支持切向压力的硬件或平台该值必须 (MUST)0

注意
尽管属性名含“pressure”,实际产生该值的硬件控制/传感器可能并非真正压力敏感。例如多数字轮可自由设定而不需持续施压维持非零位置。
tiltX

平面角(度,范围 [-90,90]),为 Y-Z 平面与包含传感器轴(如笔/触控笔)及 Y 轴的平面之间的角度。正 tiltX 向右(X 增加方向)。tiltX 可与 tiltY 组合表示相对于数字化仪法线的倾斜。未报告倾斜/角度的硬件与平台该值必须 (MUST)0

tiltX 说明图
2 tiltX
tiltY

平面角(度,范围 [-90,90]),为 X-Z 平面与包含传感器轴及 X 轴的平面之间的角度。正 tiltY 朝向用户(Y 增加方向)。tiltY 可与 tiltX 组合表示相对于数字化仪法线的倾斜。未报告倾斜/角度的硬件与平台该值必须 (MUST)0

tiltY 说明图
3 tiltY
twist

传感器(如笔/触控笔)围绕其主轴的顺时针旋转角(度,范围 [0,359])。未报告该值的硬件与平台必须 (MUST)返回 0

altitudeAngle

传感器在范围 [0,π/2] 的仰角(弧度)—— 0 表示与表面(X-Y 平面)平行,π/2 表示垂直。未报告倾斜/角度的硬件与平台该值必须 (MUST)π/2

注意
此处定义的 altitudeAngle 默认值为 π/2,表示传感器垂直于表面。这与 Touch Events - Level 2 规范中 altitudeAngle 默认值 0 不同。
altitudeAngle 说明图
4 示例: altitudeAngle = π/4(距 X-Y 平面 45 度)。
azimuthAngle

传感器的方位角(弧度),范围 [0, 2π]——0 表示传感器笔帽指向 X 增加方向(俯视时“3 点钟”位置),顺时针递增(π/2 在“6 点钟”、π 在“9 点钟”、3π/2 在“12 点钟”)。当传感器完全垂直于表面(altitudeAngleπ/2)时该值必须 (MUST)0。未报告倾斜/角度的硬件与平台必须 (MUST)0

azimuthAngle 说明图
5 示例: azimuthAngle = π/6(“4 点钟”)。
pointerType

指示导致事件的设备类型(如鼠标、笔、触摸)。如果用户代理要为鼠标、笔/触控笔或触摸输入设备触发指针事件,则 pointerType 的值必须 (MUST)符合下表:

指针设备类型 pointerType
鼠标 mouse
笔 / 触控笔 pen
触摸接触 touch

若用户代理无法检测设备类型,则该值必须 (MUST)为空字符串。若支持上述之外的指针设备类型,pointerType 的值应当 (SHOULD)加厂商前缀以避免不同设备类型名称冲突。未来规范可以 (MAY)提供其他设备类型的规范值。

注意
参见 示例 2 了解如何使用 pointerType。注意开发者应提供默认处理,以覆盖用户代理可能自定义的 pointerType 值及其为空字符串的情况。
isPrimary

指示该指针是否表示此指针类型的主指针

getCoalescedEvents()

返回合并事件列表的方法。

getPredictedEvents()

返回预测事件列表的方法。

PointerEventInit 字典用于 PointerEvent 构造函数,以提供构建不受信任(合成)指针事件的机制。它继承自 [UIEVENTS] 中定义的 MouseEventInit 字典。参见 示例 了解触发不受信任指针事件的示例代码。

事件构造步骤中,PointerEvent 会克隆 PointerEventInitcoalescedEvents合并事件列表,并克隆其 PointerEventInitpredictedEvents预测事件列表

注意
PointerEvent 接口继承自 MouseEvent(定义见 UI Events)。另请注意 CSSOM View Module 中提出的扩展:将各种坐标属性从 long 改为 double 以允许小数坐标。若用户代理对 PointerEvent 实现了此扩展而未对常规 MouseEvent 实现,则在 clickauxclickcontextmenu 事件方面有额外要求。

4.1.1 按钮状态

4.1.1.1 组合按钮交互

一些指针设备(如鼠标或触控笔)支持多个按钮。在 [UIEVENTS] 的鼠标事件模型中,每次按钮按下都会产生 mousedownmouseup。为更好抽象硬件差异并简化跨设备输入编程,Pointer Events 不会为组合按钮按压(在已有按钮按下时再按下额外按钮)触发重叠的 pointerdownpointerup

相反,可通过检查 buttonbuttons 属性的变化来检测组合按钮按压。它们继承自 MouseEvent 接口,但语义与取值在后续章节有所改变。

对指针事件修改 button / buttons 的语义仅适用于指针事件。对于 clickauxclickcontextmenu,其值必须 (MUST)遵循 [UIEVENTS],与兼容性鼠标事件一致。

4.1.1.2 button 属性

为在任意指针事件(不仅是 pointerdownpointerup)中识别按钮状态转换,button 属性表示触发事件的设备按钮状态变化。

设备按钮变化 button
自上次事件起,按钮及触摸/笔接触均无变化 -1
左键鼠标,
触摸接触,
笔接触
0
鼠标中键 1
鼠标右键,
笔筒按钮
2
X1(后退)鼠标 3
X2(前进)鼠标 4
笔橡皮擦按钮 5
注意
鼠标拖拽期间,pointermove 事件中的 button 值与 mousemove 不同。例如按住右键移动时,pointermovebutton 值为 -1,而 mousemove 的值为 2。
4.1.1.3 buttons 属性

buttons 属性以位掩码给出当前设备按钮状态(与 MouseEvent 相同,但可能值更多)。

当前设备按钮状态 buttons
鼠标移动且无按钮按下,
笔悬停且无按钮按下
0
左键鼠标,
触摸接触,
笔接触
1
鼠标中键 4
鼠标右键,
笔筒按钮
2
X1(后退)鼠标 8
X2(前进)鼠标 16
笔橡皮擦按钮 32

4.1.2 主指针

在多指针(例如多触点)场景中,isPrimary 用于在每个指针类型的一组活动指针中识别主指针。

  • 任意时刻,每种指针类型最多只有一个主指针。
  • 某指针类型第一个变为活动的指针(例如多触点交互中第一个触摸屏幕的手指)成为该类型的主指针。
  • 只有主指针会生成 兼容性鼠标事件。若存在多个主指针,它们都会生成 兼容性鼠标事件
注意
需要单指交互的作者可忽略非主指针(但参见关于多个主指针的说明)。
注意
当多个指针设备类型同时使用时(每个 pointerType 一个),多个指针被视为主指针。例如同时移动一个触摸接触与一个鼠标光标会产生两个被视为主指针的指针。
注意
某些设备、操作系统与用户代理可能忽略同时使用多种指针输入以避免误操作。例如同时支持触摸与笔的设备在使用笔时可能忽略触摸输入(俗称“掌托检测”)。作者目前无法抑制该行为。
注意
在某些情况下,用户代理可能触发没有任何指针标记为主指针的事件。例如当某类型多指同时活动且主指针被移除(离开屏幕),可能出现无主指针;或在平台依据设备上所有同类型活动指针(包括目标为其它应用的指针)确定主指针时,若首个主指针在用户代理外而其它指针在用户代理内,则用户代理可以 (MAY)为这些指针触发 isPrimaryfalse 的事件。
注意
当前操作系统与用户代理通常无多鼠标输入概念。多个鼠标设备(如笔记本触控板加外接鼠标)通常视为单一设备——任意设备移动均转为单一鼠标指针移动,按钮按下不区分来源。因此通常只有一个鼠标指针,并且该指针为主指针。

4.1.3 使用 PointerEvent 接口触发事件

触发指针事件名为 e,意为使用 PointerEventPointerEvent 接口与 属性与默认操作 中定义的属性设置来 触发事件

若该事件不是 gotpointercapturelostpointercaptureclickauxclickcontextmenu,则为此 PointerEvent 运行 处理待定指针捕获 步骤。

确定事件目标步骤如下:

targetDocument 为目标的 节点文档 [DOM]。

若事件为 pointerdownpointermovepointerup,则将该事件的 pointerId活动文档设为 targetDocument

若事件是 pointerdown,关联设备为直接操作设备且目标是 Element,则按照 隐式指针捕获 中描述,为此 pointerId 设置指针捕获为目标元素。

在触发事件之前,用户代理应当 (SHOULD)将目标视为指针从 previousTarget 移动到该目标,用于确保事件顺序 [UIEVENTS]。若 needsOverEvent 标志已设,即便目标元素相同也需要一个 pointerover

向确定的目标触发事件。

保存确定的目标为该指针的 previousTarget,并重置 needsOverEventfalse。若 previousTarget 不再 已连接 [DOM],则沿事件路径更新 previousTarget 为最近的仍 已连接 的父节点,并设置 needsOverEventtrue

注意
使用 指针捕获目标覆盖 作为目标而非常规命中测试结果时,可能触发一些边界事件(参见 [UIEVENTS])。这与指针离开其前一目标并进入新的捕获目标相同。释放捕获后,指针离开捕获目标并进入命中测试目标时也可能发生同样场景。
4.1.3.1 属性与默认操作

本规范定义的事件类型之 bubblescancelable 与默认操作见下表。每种事件类型详情参见指针事件类型

事件类型 是否冒泡 可取消 默认操作
pointerover
pointerenter
pointerdown 视情况:若指针为主指针,则与 mousedown 的所有默认操作一致
取消该事件也会阻止后续兼容性鼠标事件触发。
pointermove 视情况:若指针为主指针,则与 mousemove 默认操作一致
pointerrawupdate
pointerup 视情况:若指针为主指针,则与 mouseup 默认操作一致
pointercancel
pointerout
pointerleave
gotpointercapture
lostpointercapture

视口操作(平移与缩放)——通常是直接操作交互结果——刻意不作为指针事件默认操作,即这些行为(例如手指在触摸屏上移动导致页面滚动)不能通过取消指针事件抑制。作者必须使用 touch-action 显式声明直接操作行为。移除此对事件取消的依赖有助于用户代理性能优化。

pointerenterpointerleavecomposed [DOM] 属性应当 (SHOULD)false;对表中其它指针事件该属性应当 (SHOULD)true

对于上表所有指针事件,detail [UIEVENTS] 属性应当 (SHOULD)为 0。

注意
许多用户代理为兼容旧内容在 MouseEvent 暴露非标准属性 fromElementtoElement。建议这些用户代理在 PointerEvent 中将(继承的)这些属性值设为 null,以引导作者使用标准替代(targetrelatedTarget)。

类似 MouseEventrelatedTargetrelatedTarget 应初始化为指针刚刚离开的元素(pointeroverpointerenter)或指针正进入的元素(pointeroutpointerleave)。其它指针事件该值默认为 null。注意当元素获得指针捕获后,该指针的后续事件都视为在捕获元素边界内。

gotpointercapturelostpointercapture,除表中定义属性外,其余属性应与导致用户代理运行 处理待定指针捕获步骤并触发上述事件的指针事件一致。

4.1.3.2 处理待定指针捕获

用户代理必须 (MUST)隐式释放指针捕获时以及触发非 gotpointercapture / lostpointercapture 的指针事件时运行以下步骤。

  1. 若该指针的 指针捕获目标覆盖 已设且不等于 待定指针捕获目标覆盖,则在该 指针捕获目标覆盖节点上触发名为 lostpointercapture 的指针事件。
  2. 若该指针的 待定指针捕获目标覆盖 已设且不等于 指针捕获目标覆盖,则在该 待定指针捕获目标覆盖上触发名为 gotpointercapture 的指针事件。
  3. 指针捕获目标覆盖 设为 待定指针捕获目标覆盖(若已设),否则清除 指针捕获目标覆盖
注意

clickauxclickcontextmenu 事件章节定义,即便 lostpointercapture 已分派,对应的 clickauxclickcontextmenu(若存在)仍会分派给捕获目标。

4.1.3.3 抑制指针事件流

用户代理 检测到页面不太可能继续接收具有特定 pointerId 的指针事件时,必须 (MUST)抑制指针事件流。以下任一场景满足条件(可能还有其它场景):

  • 用户代理打开了模态对话框或菜单。
  • 指针输入设备被物理断开,或可悬停设备(如可悬停触控笔)离开数字化仪可检测的悬停范围。
  • 指针随后被用于操控页面视口(例如平移或缩放)。详见 touch-action CSS 属性章节。
    注意
    用户代理可通过多种指针类型(如触摸与笔)触发平移或缩放,因此一次平移/缩放动作可能导致抑制多个指针,包括不同类型。
  • 作为拖拽操作启动算法的一部分(参见 拖放处理模型 [HTML]),对引发该拖拽操作的指针。
注意

用户代理可以 (MAY)抑制指针事件流的其它场景包括:

  • 设备屏幕方向在指针活动时改变。
  • 用户尝试使用超过设备支持数量的同时指针输入。
  • 用户代理将输入解释为误触(例如硬件支持掌托检测)。

检测这些场景的方法超出本规范范围。

用户代理必须 (MUST)运行以下步骤以抑制指针事件流

4.1.4 由布局变化引发的边界事件

当指向设备相对屏幕表面移动或其某属性变化时,会按 指针事件类型定义触发各种事件。对于静止的指向设备(既未移动也无属性变化),在布局变化影响该指针的 用户代理必须 (MUST)触发特定边界事件以反映命中测试目标变化,参见 pointeroverpointerenterpointeroutpointerleave。出于性能(例如避免监听器引发大量命中测试或布局变更)考虑,用户代理可以 (MAY)延迟这些边界事件。

注意
静止的指向设备(既未移动也无属性变化)永远不会触发 pointermove

4.1.5 tiltX / tiltYaltitudeAngle / azimuthAngle 之间转换

Pointer Events 包含两组互补属性表达传感器相对 X-Y 平面的朝向:tiltX / tiltY(原始规范引入),以及 azimuthAngle / altitudeAngle(采纳自 Touch Events - Level 2)。

根据具体硬件与平台,用户代理通常只会接收到其中一组值(tiltX / tiltYaltitudeAngle / azimuthAngle)。用户代理必须 (MUST)使用以下算法转换这些值。

当用户代理从 azimuthAngle / altitudeAngle 计算 tiltX / tiltY 时,应当 (SHOULD)使用 Math.round [ECMASCRIPT] 规则进行整数取整。

示例 5:在 tiltX/tiltY 与 altitudeAngle/azimuthAngle 之间转换
/* 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};
}

4.2 指针事件类型

以下是本规范定义的事件类型。

主指针,这些事件(除 gotpointercapturelostpointercapture)也可能触发 兼容性鼠标事件

4.2.1 pointerover 事件

用户代理必须 (MUST)触发指针事件 pointerover,当出现以下任一情况:

4.2.2 pointerenter 事件

当发生以下任一情况时,用户代理必须 触发名为 pointerenter 的指针事件:

注意
该事件类型与 pointerover 类似,但有两点不同: pointerenter 不冒泡,且其分派会考虑包括后代元素在内的 命中测试边界。
注意
该事件类型与 [UIEVENTS] 中描述的 mouseenter 事件,以及 [CSS21] 中描述的 CSS :hover 伪类存在相似之处。另见 pointerleave 事件。

4.2.3 pointerdown 事件

当指针进入活动按钮状态时,用户代理必须 触发名为 pointerdown 的指针事件。对于鼠标,这是设备从未按下任何按钮过渡到至少按下一个按钮时。对于触摸,这是与数字化仪产生物理接触时。对于触控笔,这是触控笔在未按下任何按钮的情况下与数字化仪产生物理接触,或在悬停时从未按下任何按钮过渡到至少按下一个按钮。

注意
对于鼠标(或其他多按钮指针设备),这意味着 pointerdownpointerup 并不会在与 mousedownmouseup 完全相同的情形下触发。更多信息参见 组合按钮

对于不支持悬停的输入设备,用户代理必须在分派 pointerdown 事件之前,先分别触发名为 pointeroverpointerenter 的指针事件。

注意
作者可以通过取消 pointerdown 事件(当 isPrimary 属性为 true 时)来阻止某些兼容性鼠标事件的触发。这会在该指针上设置 PREVENT MOUSE EVENT 标志。但请注意,这并不会阻止 mouseovermouseentermouseoutmouseleave 事件的触发。

4.2.4 pointermove 事件

当指针改变任何不会触发 pointerdownpointerup 事件的属性时,用户代理必须 触发名为 pointermove 的指针事件。这包括对坐标、压力、切向压力、倾斜、旋转、接触几何(widthheight)或组合按钮的任何更改。

用户代理可以延迟分派 pointermove 事件(例如出于性能原因)。 对于单个已分派的 pointermove 事件,其合并事件信息将通过 getCoalescedEvents 方法暴露。 此类事件的最终坐标应被用于确定事件的目标。

4.2.5 pointerrawupdate 事件

当指针改变任何不会触发 pointerdownpointerup 的属性时,且仅在安全上下文中, 用户代理必须 触发名为 pointerrawupdate 的指针事件。 这些属性列表参见 pointermove 事件。

pointermove 相比,用户代理应当尽可能快且以 JavaScript 能处理的最高频率分派 pointerrawupdate 事件。

由于 pointermove 事件可能被延迟或被合并,用于确定 target 的事件最终位置可能不同于其被合并的事件,因此 pointerrawupdate 事件的 target 可能与 pointermove 的不同。

注意:如果事件循环中已经存在另一个具有相同 pointerId 且尚未分派的 pointerrawupdate,则用户代理可以将新的 pointerrawupdate 与该事件合并,而不是创建新任务。 这可能导致 pointerrawupdate 具有合并事件,并且它们会在该事件在事件循环中被处理时,作为单个 pointerrawupdate合并事件一并投递。更多信息参见 getCoalescedEvents

关于 pointerrawupdatepointermove 的顺序:如果用户代理从平台收到的更新会导致两者都触发,则用户代理必须先分派 pointerrawupdate,再分派相应的 pointermove

target 外,自上一次 pointermove 起已分派的所有 pointerrawupdate 的合并事件列表的串联,在其它事件属性方面,与下一次 pointermove 的合并事件相同。 pointerrawupdate 的属性与 pointermove 基本一致,差别在于 cancelable 对于 pointerrawupdate必须为 false。

用户代理应当不为 兼容性鼠标事件触发 pointerrawupdate

注意
pointerrawupdate 添加监听器可能会对网页性能产生负面影响,具体取决于用户代理的实现。 对于大多数用例,其他指针事件类型已足够。 只有当 JavaScript 需要高频事件且能同样快速处理时,才应添加 pointerrawupdate 监听器。 在这些情况下,很可能无需监听其他类型的指针事件。

4.2.6 pointerup 事件

当指针离开活动按钮状态时,用户代理必须 触发名为 pointerup 的指针事件。对于鼠标,这是从至少一个按钮按下过渡为没有按钮按下。对于触摸,这是与数字化仪解除物理接触。对于触控笔,这是在没有按钮按下的情况下,触控笔与数字化仪解除物理接触,或在悬停时从至少一个按钮按下过渡为没有按钮按下。

对于不支持悬停的输入设备,用户代理必须在分派 pointerup 事件之后,依次触发名为 pointeroutpointerleave 的指针事件。

所有 pointerup 事件的 pressure 值均为 0

如果当前已捕获该指针,用户代理必须在此时隐式释放指针捕获

注意
对于鼠标(或其他多按钮指针设备),这意味着 pointerdownpointerup 并不会在与 mousedownmouseup 完全相同的情形下触发。更多信息参见 组合按钮

4.2.7 pointercancel 事件

当检测到需要抑制指针事件流的场景时,用户代理必须 触发名为 pointercancel 的指针事件。

pointercancel 事件下列属性的值必须与具有相同 pointerId 的上一个已分派指针事件的值相匹配:widthheightpressuretangentialPressuretiltXtiltYtwistaltitudeAngleazimuthAnglepointerTypeisPrimary,以及从 [UIEVENTS] 继承的坐标。pointercancel 事件中的 coalescedEventspredictedEvents 列表必须为空,且该事件的 cancelable 属性必须为 false。

4.2.8 pointerout 事件

当发生以下任一情况时,用户代理必须 触发名为 pointerout 的指针事件:

4.2.9 pointerleave 事件

当发生以下任一情况时,用户代理必须 触发名为 pointerleave 的指针事件:

注意
该事件类型与 pointerout 类似,但有两点不同: pointerleave 不冒泡,且其分派会考虑包括后代元素在内的 命中测试边界。
注意
该事件类型与 [UIEVENTS] 中描述的 mouseleave 事件,以及 [CSS21] 中描述的 CSS :hover 伪类存在相似之处。另见 pointerenter 事件。

4.2.10 gotpointercapture 事件

当某元素获得指针捕获时,用户代理必须 触发名为 gotpointercapture 的指针事件。该事件在接收指针捕获的元素上触发。随后该指针的事件将投递到该元素。参见 设置指针捕获处理待定指针捕获 章节。

4.2.11 lostpointercapture 事件

当某个指针的指针捕获被释放后,用户代理必须 触发名为 lostpointercapture 的指针事件。该事件必须在捕获释放后该指针的任何后续事件之前触发。该事件在移除指针捕获的元素上触发。除 clickauxclickcontextmenu 事件外,该指针的后续事件将遵循常规命中测试机制(不在本规范范围内)来确定事件目标。参见 释放指针捕获隐式释放指针捕获处理待定指针捕获 章节。

4.2.12 clickauxclickcontextmenu 事件

本节对 [UIEVENTS] 中定义的 clickauxclickcontextmenu 事件作出补充。这些事件通常与用户界面激活相关,即使是来自非指针输入设备(如键盘)也会触发。

这些事件必须PointerEvent 类型,并受本节其余部分所述附加要求的约束。

4.2.12.1 事件属性

对于这些事件,除 pointerIdpointerType 外,所有本规范定义的 PointerEvent 特定属性必须取其默认值。此外:

  • 若这些事件由指向设备生成,其 pointerIdpointerType必须与引发这些事件的 PointerEvents 相同。
  • 若这些事件由非指向设备(如语音识别软件或键盘交互)生成,则 pointerId必须-1,且 pointerType必须为空字符串。
4.2.12.2 事件坐标

PointerEvent 所述,CSSOM View Module 提议将各类坐标属性(screenXscreenYpageXpageYclientXclientYxyoffsetXoffsetY)重新定义为 double,以允许小数坐标。 然而,如果仅对 PointerEvent 而非常规的 MouseEvent 应用此更改,则在 clickauxclickcontextmenu 的情形下被证明会导致与旧代码的 Web 兼容性问题。为此,仅对 PointerEvent 实现了该提议更改的用户代理必须使用 Math.floor [ECMASCRIPT] 将 clickauxclickcontextmenu 的各类坐标属性转换为 long 值(如原始 UI Events 中定义)。

4.2.12.3 事件分派

clickauxclickcontextmenu 事件必须遵循 [UIEVENTS] 规范中定义的分派流程,但事件目标会通过下述算法覆盖:

  1. event 为正在分派的 clickauxclickcontextmenu 事件,令 userEvent 为引发 event 触发的用户交互事件。

    注意

    userEvent 可能不是 PointerEvent;例如,在复选框元素上按空格键导致分派 click 事件时,userEventKeyboardEvent

    userEventPointerEvent 时,对于 clickauxclick 事件,userEventpointerup;对于 contextmenu 事件,userEventpointerdownpointerup(取决于原生平台约定)。

  2. 如果 userEvent 不是 PointerEvent,则按照 [UIEVENTS] 规范分派 event,且不覆盖 event 的目标,并跳过下述其余步骤。
  3. 如下定义 target

    如果 eventcontextmenu 事件,或 userEvent 分派时相应的指针处于已捕获状态,则令 targetuserEvent 的目标。

    否则(eventclickauxclick 事件且 userEvent 为未捕获分派的 pointerup 事件),令 target 为分派 event 时 DOM 中相应 pointerdownpointerup 目标的最近公共(包含)祖先。

  4. 按照 [UIEVENTS] 规范,将 event 分派到 target

    注意
    如果 userEvent 是在捕获状态下发生的,即使已分派了具有相同 pointerIdlostpointercapture 事件,event 仍会分派到 userEvent 的捕获目标。

5. Element 接口的扩展

以下章节描述对现有 Element 接口的扩展,以便于设置与释放指针捕获。

WebIDLpartial interface Element {
  undefined setPointerCapture (long pointerId);
  undefined releasePointerCapture (long pointerId);
  boolean hasPointerCapture (long pointerId);
};
setPointerCapture()

为参数 pointerId 标识的指针,在调用此方法的元素上设置指针捕获。对于该指针的后续事件,捕获目标将取代正常的命中测试结果,仿佛指针始终位于捕获目标之上,并且它们必须始终定向到此元素,直到捕获被释放。为使此方法生效,指针必须处于其活动按钮状态,否则将静默失败。当所提供的方法参数与任何活动指针均不匹配时,throw 一个 "NotFoundError" DOMException

releasePointerCapture()

从调用此方法的元素上,为参数 pointerId 标识的指针释放指针捕获。该指针的后续事件将遵循常规的命中测试机制(不在本规范范围内)来确定事件目标。当所提供的方法参数与任何活动指针均不匹配时,throw 一个 "NotFoundError" DOMException

hasPointerCapture

指示调用此方法的元素是否对参数 pointerId 标识的指针具有指针捕获。具体而言,若 待定指针捕获目标覆盖 对于 pointerId 已设置为调用此方法的元素,则返回 true;否则返回 false

注意
即使该元素尚未接收到 gotpointercapture 事件,在调用 setPointerCapture() 之后,此方法会立即返回 true。因此,它可用于在 pointerdown 事件监听器内部检测隐式指针捕获

6. GlobalEventHandlers 混入的扩展

以下章节描述对现有 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;
};
onpointerover
事件处理程序 IDL 属性,对应 pointerover 事件类型。
onpointerenter
事件处理程序 IDL 属性,对应 pointerenter 事件类型。
onpointerdown
事件处理程序 IDL 属性,对应 pointerdown 事件类型。
onpointermove
事件处理程序 IDL 属性,对应 pointermove 事件类型。
onpointerrawupdate
事件处理程序 IDL 属性,对应 pointerrawupdate 事件类型。
onpointerup
事件处理程序 IDL 属性,对应 pointerup 事件类型。
onpointercancel
事件处理程序 IDL 属性,对应 pointercancel 事件类型。
onpointerout
事件处理程序 IDL 属性,对应 pointerout 事件类型。
onpointerleave
事件处理程序 IDL 属性,对应 pointerleave 事件类型。
ongotpointercapture
事件处理程序 IDL 属性,对应 gotpointercapture 事件类型。
onlostpointercapture
事件处理程序 IDL 属性,对应 lostpointercapture 事件类型。

7. Navigator 接口的扩展

Navigator 接口定义见 [HTML]。本规范扩展 Navigator 接口以提供设备检测支持。

WebIDLpartial interface Navigator {
    readonly  attribute long maxTouchPoints;
};
maxTouchPoints

设备所支持的同时触摸接触点的最大数量。对于具有多个数字化仪的设备(例如多个触摸屏),该值必须 为每个数字化仪所支持最大接触点数量集合中的最大值。

例如,某设备具有 3 个触摸屏,分别支持 2、5 和 10 个同时触摸接触点。则 maxTouchPoints 的值应为 10

注意
maxTouchPoints 大于 0 时表明用户的设备能够支持触摸输入,但这并不必然意味着用户 使用触摸输入。作者还应谨慎考虑系统上可能存在的其他输入方式,例如鼠标、笔或屏幕阅读器。
注意
maxTouchPoints 通常用于确保内容的交互模型能被当前硬件识别。对于能力较弱的硬件,可以为用户提供相应的 UI 便利。在无法得知精确触点数量的平台上,会提供保证可识别的最小数量。因此,已识别的触点数量有可能超过 maxTouchPoints 的值。

8. 声明直接操作行为

属性与默认操作 中所述,视口操作(平移与缩放)不能通过取消某个指针事件来抑制。相反,作者必须使用 touch-action CSS 属性声明性地定义允许哪些行为、抑制哪些行为。

注意
虽然用于操控视口的指针问题通常局限于触摸输入(用户的手指既可与内容交互,又可对页面进行平移/缩放),但某些用户代理也可能允许其他指针类型进行相同类型的(直接或间接)操作。例如,在移动/平板设备上,用户也可以使用触控笔滚动。尽管出于历史原因,本规范中定义的 touch-action CSS 属性似乎只与触摸输入相关,但实际上它适用于所有能够进行直接操作以进行平移和缩放的各种指针输入。

8.1 touch-action CSS 属性

名称: touch-action
取值: auto | none | [ pan-x || pan-y ] | manipulation
初始值: auto
适用元素: 除:非替换内联元素、表格行、行组、表格列与列组 之外的所有元素
是否继承:
百分比: 不适用
媒体: 视觉
计算值: 与指定值相同
规范顺序: 按语法
动画类型: 不可动画

touch-action CSS 属性决定直接操作交互(尽管属性名如此,但不限于触摸)可以触发用户代理的平移与缩放行为。参见 touch-action 的取值 一节。

在开始平移或缩放之前,若以下条件均为真,用户代理必须抑制指针事件流

注意
一些用户代理对某些行为实现了复杂手势,这些行为包含一系列独立的离散手势,但会被视为单一连续手势的一部分。举例来说,考虑触摸屏上的“甩动滚动”手势:用户以快速手指移动开始平移文档,抬起手指后,文档仍以模拟惯性继续平移。当文档仍在移动时,用户可能再次将手指放到触摸屏上,再执行一次“甩动”以进一步增加平移动量,或反向平移以减速、停止,甚至反转方向。由于本规范不具有手势与行为实现方面的规范性定义,是否在第二次触摸(在其被解释为第二次“甩动”或对当前平移的抵消之前)触发指针事件,由用户代理自行决定。
注意
touch-action 不会应用/级联到嵌入的浏览上下文中。例如,即便对一个 <iframe> 应用了 touch-action,也不会对该 <iframe> 内的平移与缩放直接操作行为产生任何影响。

8.2 确定支持的直接操作行为

当用户使用直接操作指针(如触摸屏上的触摸或触控笔)与某元素交互时,该输入的效果由 touch-action 属性的值,以及该元素与其祖先的默认直接操作行为共同决定,如下:

注意
一些用户代理支持涉及多个并发指针(例如多点触控)的平移与缩放交互。对多个并发指针的 touch-action 取值进行处理或关联的方法超出本规范范围。

8.3 touch-action 值的详细说明

touch-action 属性涵盖与视口平移与缩放相关的直接操作行为。任何其他用户代理行为(例如文本选择/高亮,或激活链接与表单控件)不得受该 CSS 属性影响。

注意
术语“panning(平移)”与“scrolling(滚动)”视为同义(更准确地说,“平移”是使用直接操作输入的“滚动”)。用于触发平移/滚动的交互或手势,以及用于触发 autonone 取值行为的交互或手势,均不在本规范范围内。
auto
用户代理可以考虑从该元素开始、与视口平移和缩放相关的任何被允许的直接操作行为。
none
从该元素开始的直接操作交互不得触发与视口平移和缩放相关的行为。
pan-x
pan-y
用户代理可以仅在列出的所有方向中任一方向上开始平移的目的下,考虑从该元素开始的直接操作交互。一旦平移已开始,即使不允许以相反方向开始平移,用户也可以反转方向。与此相反,当平移被限制在单一轴上(使用 pan-xpan-y)时,平移过程中不能改变轴。
manipulation
用户代理可以仅为平移与连续缩放(如捏合缩放)的目的,考虑从该元素开始的直接操作交互;但不得触发其他依赖在限定时间内进行多次触发的相关行为(例如双击缩放,或双击并按住进行单指缩放)。
注意
touch-action 属性仅适用于同时支持 CSS widthheight 属性的元素(见 [CSS21])。该限制旨在便于用户代理针对低延迟直接操作平移与缩放进行优化。对于默认不支持的元素(如 <span>,它是非替换内联元素),作者可以将其 display CSS 属性设置为支持 widthheight 的值(如 block)。未来规范可能会将该 API 扩展至所有元素。
注意

方向特定的 pan 取值有助于自定义某些过度滚动行为。例如,为实现简单的“下拉刷新”,当滚动位置为 0 时,可将文档的 touch-action 设为 pan-x pan-down,否则设为 pan-x pan-y。 这样允许指针事件处理程序定义从文档顶部开始的向上平移/滚动的行为。

方向特定的 pan 取值还可用于在原生可滚动的元素内部组合一个通过指针事件处理实现自定义平移的组件(或反之亦然)。例如,图片轮播组件可以使用 pan-y 来确保其在任何水平平移操作时接收指针事件,而不干扰文档的垂直平移。当轮播到达最右端时,它可以将自己的 touch-action 改为 pan-y pan-right,以便在其范围之外的后续滚动操作(如果可能)可以滚动视口中的文档。进行中的平移/滚动操作无法改变其行为。

注意
禁用某些用于平移与缩放的默认直接操作行为,可能使用户代理能更快速地响应其他行为。例如,在 auto 下,用户代理通常会在 click 之前增加 300ms 延迟,以便处理双击手势。在这些情况下,显式设置 touch-action: nonetouch-action: manipulation 将移除此延迟。注意,确定点击或双击手势的方法超出本规范范围。
示例 6:不允许任何直接操作行为
<div style="touch-action: none;">
    This element receives pointer events for all direct manipulation interactions that otherwise lead to panning or zooming.
</div>
示例 7:仅允许水平平移
<div style="touch-action: pan-x;">
    This element receives pointer events when not panning in the horizontal direction.
</div>
示例 8:禁止子区域的平移与缩放直接操作行为
<div style="overflow: auto;">
    <div style="touch-action: none;">
        This element receives pointer events for all direct manipulation interactions that otherwise lead to panning or zooming.
    </div>
    <div>
        Direct manipulation interactions on this element MAY be consumed for manipulating the parent.
    </div>
</div>
示例 9: 禁止平移与缩放直接操作的中间父元素
<div style="overflow: auto;">
    <div style="touch-action: pan-y;">
        <div style="touch-action: pan-x;">
            This element receives pointer events for all direct manipulation interactions because
            it allows only horizontal panning yet an intermediate ancestor
            (between it and the scrollable element) only allows vertical panning.
            Therefore, no direct manipulation behaviors for panning/zooming are
            handled by the user agent.
        </div>
    </div>
</div>

9. 指针捕获

9.1 简介

本节为非规范性内容。

指针捕获允许特定指针(包括任何兼容性鼠标事件)的事件被重定向到正常命中测试结果之外的特定元素。这在自定义滑块控件等场景中很有用(例如类似 [HTML] <input type="range"> 控件)。可以在滑块拇指元素上设置指针捕获,使用户即便将指针滑出拇指区域,也能来回拖动控件。

Custom Volume Slider
6 自定义滑块控件示例:通过来回滑动拇指元素选择数值。在拇指上触发 pointerdown 之后,可以使用指针捕获,让用户即使指针稍微偏离拇指,也能继续滑动。

9.2 设置指针捕获

通过调用 element.setPointerCapture(pointerId) 方法,可在类型为 elementElement 上设置指针捕获。 当调用此方法时,用户代理必须执行以下步骤:

  1. 若方法参数提供的 pointerId 与任何活动指针均不匹配,则抛出NotFoundErrorDOMException
  2. pointer 为由给定 pointerId 指定的活动指针
  3. element已连接 [DOM],则抛出InvalidStateErrorDOMException
  4. 若在该方法被调用时,element节点文档 [DOM] 具有锁定元素([PointerLock] 的 pointerLockElement),则抛出InvalidStateErrorDOMException
  5. pointer 不处于活动按钮状态,或 element节点文档不是该 pointer活动文档,则终止这些步骤。
  6. 对于指定的 pointerId,将待定指针捕获目标覆盖设置为调用该方法的 Element
注意
当一次设置或释放指针捕获的调用仍处于待处理状态时(见 处理待定指针捕获),若再次调用设置或释放,则只要第二次调用成功,第二次调用就会覆盖第一次调用;否则第一次调用仍然有效。这同样适用于在 pointerdown 监听器中尝试释放隐式指针捕获但失败的情形。

9.3 释放指针捕获

通过调用 element.releasePointerCapture(pointerId) 方法可在元素上显式释放指针捕获。调用该方法时,用户代理必须执行以下步骤:

  1. 若方法参数提供的 pointerId 与任何活动指针均不匹配,且这些步骤并非因隐式释放指针捕获而被调用,则抛出NotFoundErrorDOMException
  2. 若对具有指定 pointerIdElement,其 hasPointerCapture 为 false,则终止这些步骤。
  3. 对于指定的 pointerId,若已设置,则清除 待定指针捕获目标覆盖
注意
参见 设置指针捕获 一节中的说明。

9.4 隐式指针捕获

实现用于平移与缩放的直接操作交互的输入(如触摸屏上的触摸或触控笔)应当表现得与在调用任何 pointerdown 监听器之前,在目标元素上调用了 setPointerCapture 完全一致。可使用 hasPointerCapture API(例如在 pointerdown 监听器中)来判断此情况是否已发生。如果在下一个指针事件触发之前未对该指针调用 releasePointerCapture,则将按常规向目标分派 gotpointercapture 事件以表明捕获已生效。

注意
这与 [PointerEvents] 存在不兼容变更,但对绝大多数现有内容无影响。除与典型平台 UX 约定一致外,此隐式捕获设计还允许用户代理进行性能优化:无需在未显式开发者选择加入的情况下对触摸移动事件执行命中测试(与现有主流原生与 Web 触摸输入 API 的性能特性一致)。
注意
此外,用户代理还可以在特定 UI 小部件(如范围输入控件)上对所有输入设备实现隐式指针捕获(允许交互过程中手指移动略微偏离控件本身)。

9.5 隐式释放指针捕获

在分派完 pointeruppointercancel 事件后,用户代理必须清除该刚刚分派的 pointeruppointercancel 事件对应的 pointerId待定指针捕获目标覆盖, 随后运行 处理待定指针捕获 步骤,以在必要时触发 lostpointercapture。 完成 处理待定指针捕获 步骤后,若该指针支持悬停,用户代理必须另外发送相应的边界事件,以反映无捕获状态下指针的当前位置。

指针捕获目标覆盖不再已连接 [DOM] 时,指针捕获目标覆盖应当设置为文档。

待定指针捕获目标覆盖不再已连接 [DOM] 时,应清除此 待定指针捕获目标覆盖 节点。

注意
上述两段的结果是:在捕获节点被移除后的下一次 处理待定指针捕获 过程中,会在文档上触发与已捕获指针相对应的 lostpointercapture 事件。

当在某元素上成功应用指针锁 [PointerLock] 时,若当前有任何元素已设置捕获或处于待捕获状态,用户代理必须按调用了 releasePointerCapture 方法的方式运行相应步骤。

10. 合并事件与预测事件

注意
本规范不定义用户代理应如何合并或预测指针移动数据,只规定访问这些信息的 API。

10.1 合并事件

出于性能原因,用户代理可能选择不在指针的每一次 pointermove 发生时都发送事件,即每次某个 可度量属性 (如坐标、压力、切向压力、倾斜、旋转或接触几何)更新时都发送。相反,它们可以将多次变化合并为单个 pointermovepointerrawupdate 事件。此方式有助于减少用户代理必须执行的事件处理量(必须执行的处理),但会自然降低追踪指针位置的粒度与精度,尤其在快速且幅度大的移动中。使用 getCoalescedEvents 方法,应用可以访问原始的、未合并的位置变化,从而获得更精细的指针移动处理。在绘图应用中,未合并事件可用于绘制更平滑的曲线,更贴近指针的真实移动。

曲线的局部视图,显示合并与未合并的点
7 绘图应用中曲线示例——仅使用来自 pointermove 事件的合并坐标(灰色圆点)时,曲线明显更棱角且粗糙;使用 getCoalescedEvents() 提供的更细粒度红色圆点绘制出的同一条线则更加平滑,更接近实际指针移动。

PointerEvent 具有关联的 合并事件列表(零个或多个 PointerEvent 的列表)。对于可信的 pointermovepointerrawupdate 事件,该列表是所有被合并进该事件的 PointerEvent 序列。“父”可信 pointermovepointerrawupdate 事件表示这些合并事件的累积,但可能经过额外处理(例如与显示刷新率对齐)。因此,这些事件的合并事件列表总是至少包含一个事件。对其它可信事件类型,该列表为空。非可信事件的 合并事件列表初始化为传入构造函数的值。

注意
由于可信父事件是合并事件的摘要或聚合,开发者通常只需要处理父事件或全部合并事件,而无需同时处理两者。
注意
当包含 合并事件列表 的可信事件被 JavaScript 重新分派时,事件分派算法会将该事件的 isTrusted 置为 false,但列表中事件原本为 true 的位保持不变。

可信事件的合并事件列表中的事件具有以下特征:

示例 10:使用合并事件列表的基础 canvas 绘图应用
<style>
    /* 禁用内置的用户代理直接操作行为(例如平移或缩放),将画布上的所有事件都交给应用。 */

    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>
注意
PointerEvent 的各属性会以最能代表合并事件列表中事件的方式进行初始化。用户代理应如何具体实现不在本规范覆盖范围内。

所有这些已分派事件的顺序必须与原始事件的真实发生顺序一致。举例:如果一次 pointerdown 导致合并的 pointermove 事件分派,则用户代理必须先分派一个包含该 pointerId 所有合并事件的 pointermove,然后再分派 pointerdown

注意

以下示例展示按递增 timeStamp 值发生的实际事件与用户代理分派事件:

实际事件 已分派事件
pointer (pointerId=2) 坐标变化 pointerrawupdate (pointerId=2) 含 1 个合并事件
pointer (pointerId=1) 坐标变化 pointerrawupdate (pointerId=1) 含 1 个合并事件
pointer (pointerId=2) 坐标变化 pointerrawupdate (pointerId=2) 含 1 个合并事件
pointer (pointerId=2) 坐标变化 pointerrawupdate (pointerId=2) 含 1 个合并事件
pointer (pointerId=1) 坐标变化 pointerrawupdate (pointerId=1) 含 1 个合并事件
pointer (pointerId=2) 坐标变化 pointerrawupdate (pointerId=2) 含 1 个合并事件
pointer (pointerId=1) 按钮按下 pointermove (pointerId=1) 含 2 个合并事件
pointermove (pointerId=2) 含 4 个合并事件
pointerdown (pointerId=1) 含 0 个合并事件
pointer (pointerId=2) 坐标变化 pointerrawupdate (pointerId=2) 含 1 个合并事件
pointer (pointerId=2) 坐标变化 pointerrawupdate (pointerId=2) 含 1 个合并事件
pointer (pointerId=1) 按钮释放 pointermove (pointerId=2) 含 2 个合并事件
pointerup (pointerId=1) 含 0 个合并事件

10.2 预测事件

一些用户代理具备内建算法,在一系列已确认指针移动之后,可以依据当前手势之前的事件及移动速度/轨迹,对未来指针位置做出预测。应用可利用 getPredictedEvents 方法使用这些信息,提前“预绘”到预测位置,以降低感知延迟,并在收到真实点后丢弃这些预测点。

使用合并点绘制的一条线,展示预测的未来点
8 绘图应用中一条线(从左下至右上的绘制手势结果)的示例:使用来自 pointermove 事件的合并坐标,并显示用户代理预测的未来点(灰色圆点)。

PointerEvent 具有关联的 预测事件列表(零个或多个 PointerEvent)。对于可信的 pointermove 事件,它是用户代理预测将要跟随该事件发生的一系列 PointerEvent;其它可信事件类型则为空。非可信事件的 预测事件列表初始化为构造函数传入的值。

注意

尽管 pointerrawupdate 事件可能拥有非空的 合并事件列表,其 预测事件列表通常出于性能考虑保持为空。

注意
当包含 预测事件列表 的可信事件被 JavaScript 重新分派时,事件分派算法会将该事件的 isTrusted 置为 false,但列表中事件原本为 true 的位保持不变。

列表中的事件数量以及它们距离当前时间戳的时间范围由用户代理及其预测算法决定。

可信事件的预测事件列表中的事件具有以下特征:

注意

作者应仅将预测事件视为在下一次指针事件分派之前的有效预测。根据用户代理预测的未来时间范围,常规指针事件可能早于一个或多个预测事件的时间戳被分派。

示例 11:使用合并与预测事件进行绘制的概念性方法

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);
    }
});

10.3 填充与维护合并事件列表和预测事件列表

当可信 PointerEvent 被创建时,用户代理应当针对其 合并事件列表预测事件列表中的每个事件执行以下步骤:

  1. 将事件的 pointerIdpointerTypeisPrimary 以及 isTrusted 设置为与“父”指针事件对应属性一致。
  2. 将事件的 cancelablebubbles 设为 false(这些事件永远不会单独分派)。
  3. 将事件的 合并事件列表预测事件列表 设为空列表。
  4. 将其它所有属性初始化为默认 PointerEvent 值。

当可信 PointerEventtarget 被改变时,用户代理应当对其 合并事件列表预测事件列表中的每个事件执行:

  1. 将事件的 target 设置为与“父”指针事件的 target 相同。

11. 与鼠标事件的兼容性映射

当今绝大多数现存 Web 内容仅使用 Mouse Events 编码。以下描述用户代理如何将通用指针输入映射为鼠标事件以兼容这些内容的算法。

与鼠标事件的兼容性映射是本规范的一个可选功能。建议用户代理支持此功能以获得对遗留内容的最佳兼容性。

注意

在高层概念上,兼容性鼠标事件旨在与相应的指针事件“交织”出现。然而,这一特定顺序并非强制,支持兼容性鼠标事件的用户代理可以延迟或批量分派鼠标事件,只要它们的相对顺序保持一致。

尤其在触摸屏输入情况下,用户代理可以采用额外启发式进行手势识别(除非被作者通过 touch-action 显式抑制)。在一次从 pointerdownpointerup 的事件序列中,手势识别可能需要等到 pointerup 事件才能确认或忽略某手势。结果是整个序列的兼容性鼠标事件可能在最后一个 pointerup 事件后一起分派,如果用户代理判定该交互不属于特定手势。这些手势识别细节不在本规范范围内,各实现可能不同。

无论是否支持兼容性鼠标事件,用户代理必须始终支持 clickauxclickcontextmenu 事件,因为它们属于 PointerEvent 类型,因此不是 兼容性鼠标事件。在指针事件中调用 preventDefault 不得影响是否触发 clickauxclickcontextmenu

注意

某些高层事件(如 contextmenufocusblur)与指针事件的相对顺序未定义,且在不同用户代理间差异存在。例如,某些用户代理中 contextmenu 常常跟随 pointerup,而另一些则常常先于 pointeruppointercancel,还有情形可能在无任何对应指针事件下触发(例如由键盘交互导致)。

此外,用户代理可能应用自身启发式来决定是否应触发 clickauxclickcontextmenu。某些用户代理可能在存在其它(非主)同类型指针或其它类型主指针时选择不触发;也可能认定某动作不是“干净”的点击/长按(例如触摸屏上手指接触期间移动过多)而决定不触发这些事件。这些行为不在本规范定义范围内,可能因实现不同而不同。

除非另有说明,任何映射的鼠标事件的目标应当与其对应指针事件的目标相同,除非该目标已不再参与其 ownerDocument 的树。在这种情况下,鼠标事件应在其被移除时仍在树中的最近祖先节点上触发,并基于新目标节点构建新的事件路径。

作者可以通过取消 pointerdown 事件来阻止产生某些兼容性鼠标事件。

只有当指针处于按下状态时才能阻止鼠标事件产生。悬停指针(例如按钮未按下的鼠标)其鼠标事件不可被阻止。

mouseovermouseoutmouseentermouseleave 事件永远不会被阻止(即使指针按下)。

当指针事件的 EventListener 被设置为 passive 时,兼容性鼠标事件无法被阻止 [DOM]。

11.1 跟踪传统鼠标指针的有效位置

尽管只有 主指针 能产生兼容性鼠标事件,但 多个主指针 可以同时激活,各自生成兼容性鼠标事件。为兼容依赖 MouseEvents 的脚本,鼠标过渡事件(mouseovermouseoutmouseentermouseleave应当模拟单一传统鼠标输入的移动。这意味着每个事件目标的进入/离开状态符合 [UIEVENTS]。用户代理应当通过在文档中维护 传统鼠标指针的有效位置 来保证这一点。

在触发 pointerdownpointeruppointermove 或在 window 上触发 pointerleave 事件之前,用户代理应当执行以下步骤:

  1. T 为即将分派的 pointerdownpointeruppointermove 事件的目标;对 pointerleave 事件,取消设置 T
  2. 如果 T 与当前 传统鼠标指针的有效位置 都未设置或相等,则结束步骤。
  3. 按照 [UIEVENTS] 分派 mouseovermouseoutmouseentermouseleave,模拟从当前 传统鼠标指针的有效位置 移动到 T 的鼠标。将未设置的当前位置或 T 视作窗口外位置。
  4. 传统鼠标指针的有效位置 设为 T
注意

传统鼠标指针的有效位置 模型反映无法总是将指针过渡事件(pointeroverpointeroutpointerenterpointerleave)直接映射到相应的传统鼠标过渡事件(mouseovermouseoutmouseentermouseleave)。下方动画展示用户代理为了用单一传统鼠标输入协调两个主指针,需要分派比指针过渡事件更多的传统鼠标过渡事件的情形。

9 同时出现的鼠标指针(白色光标)与触摸指针(白色“手”指针)导致单一传统鼠标输入(橙色光标)在两者之间移动。

在该动画中,注意鼠标点击与触摸点击之间的时间段。按钮 1 未接收 pointerout(因为“真实”鼠标指针在此期间未离开按钮矩形),但当触摸点击使 传统鼠标指针的有效位置 移动到按钮 2 时,按钮 1 接收 mouseout。类似地,在触摸点击与鼠标离开按钮 1 之前的时间段,按钮 1 未接收 pointerover,但当 传统鼠标指针的有效位置 回到按钮 1 内部时,按钮 1 接收 mouseover

11.2 支持悬停设备的映射

每当用户代理要为支持悬停的设备分派指针事件时,应当执行以下步骤:

  1. 如果该指针事件的 isPrimary 属性为 false,则分派该指针事件并结束。
  2. 如果要分派的是 pointerdownpointeruppointermove,或在 window 上的 pointerleave,则按照 跟踪传统鼠标指针的有效位置 描述分派兼容性鼠标过渡事件。
  3. 分派指针事件。
  4. 如果刚分派的是 pointerdown 且事件被 取消,则为该 pointerType 设置 PREVENT MOUSE EVENT 标志。
  5. 如果该 pointerType 未设置 PREVENT MOUSE EVENT 标志,且刚分派的事件是:
  6. 如果刚分派的是 pointeruppointercancel,则清除该 pointerTypePREVENT MOUSE EVENT 标志。

11.3 不支持悬停设备的映射

某些设备(如多数触摸屏)不支持在非活动状态悬停一个坐标(或坐标集合)。大量仅编码为鼠标事件的现有内容假设事件来自鼠标,因此以下性质通常成立:

注意
悬停常用于切换为鼠标设计的内容中的 UI 元素可见性(例如“悬停菜单”)。此类内容往往与 不支持悬停的设备 不兼容。本规范未为该场景定义兼容性映射或行为,未来版本将考虑。

这要求用户代理为此类输入设备提供不同的映射。每当用户代理要为 不支持悬停 的设备分派指针事件时,应当执行以下步骤:

  1. 如果该指针事件的 isPrimary 属性为 false,则分派该指针事件并结束。
  2. 如果要分派的是 pointerover 且尚未为该指针分派 pointerdown,则触发一次 mousemove(与遗留仅鼠标代码兼容)。
  3. 如果要分派的是 pointerdownpointeruppointermove 或在 window 上的 pointerleave,则按 跟踪传统鼠标指针的有效位置 描述分派兼容性鼠标过渡事件。
  4. 分派指针事件。
  5. 如果刚分派的是 pointerdown 且事件被 取消,则为该 pointerType 设置 PREVENT MOUSE EVENT 标志。
  6. 如果该 pointerType 未设置 PREVENT MOUSE EVENT 标志,且刚分派的事件是:
  7. 如果刚分派的是 pointeruppointercancel,则清除该 pointerTypePREVENT MOUSE EVENT 标志。

如果用户代理同时支持 Touch Events(见 [TOUCH-EVENTS])与 Pointer Events,用户代理不得同时生成本节描述的兼容性鼠标事件以及 fallback mouse events(见 [TOUCH-EVENTS])。

注意

使用一个不支持悬停的主指针(如单指触摸屏)激活元素(发生 click)通常产生以下事件序列:

  1. mousemove
  2. pointerover
  3. pointerenter
  4. mouseover
  5. mouseenter
  6. pointerdown
  7. mousedown
  8. 零个或多个 pointermovemousemove(取决于指针移动)
  9. pointerup
  10. mouseup
  11. pointerout
  12. pointerleave
  13. mouseout
  14. mouseleave
  15. click

如果在此交互期间 pointerdown取消,则事件序列变为:

  1. mousemove
  2. pointerover
  3. pointerenter
  4. mouseover
  5. mouseenter
  6. pointerdown
  7. 零个或多个 pointermove(取决于指针移动)
  8. pointerup
  9. pointerout
  10. pointerleave
  11. mouseout
  12. mouseleave
  13. click

12. 安全与隐私注意事项

本附录讨论 Pointer Events 实现的安全与隐私注意事项。讨论范围仅限于直接源自本规范所定义的事件模型、API 与事件实现的安全和隐私问题。

本规范中所定义的许多事件类型是响应用户行为而分派的。这使得恶意的事件监听器可以获取用户通常认为是机密的信息,例如用户在页面交互时鼠标 / 触控笔 / 手指的精确路径与移动轨迹。

指针事件包含额外信息(取决于用户设备支持),例如笔输入的角度或倾斜、接触面的几何形状、以及对触控笔或触摸屏施加的压力。关于角度、倾斜、几何和压力的信息与用户设备上的传感器直接相关,这意味着本规范允许某个来源访问这些传感器。

这些传感器数据,以及判定所用输入机制类型(鼠标、触摸、笔)的能力,都可能被用来推断用户或用户设备/环境的特征。这些推断出的特征以及任何设备/环境信息本身可能是敏感的——例如,它们可能让恶意站点进一步推断用户是否在使用辅助技术。这些信息还可能被用于建立用户画像、以及尝试“指纹识别”并追踪某个特定用户。

作为缓解措施,用户代理可考虑提供让用户禁用访问特定传感器数据(如角度、倾斜、压力)的能力,和/或仅在用户明确选择加入后才提供这些数据。

传感器的出厂校准信息 可能基于传感器数据的特定波动与特征用于对单个设备进行指纹识别。虽然本规范将许多与传感器相关的事件属性定义为 floatdouble 精度,但我们建议实现限制其暴露的传感器数据到实际有用的精度:

本规范定义了作者访问“预测事件”的方法。本规范本身不定义用户代理用于预测的算法。规范作者设想这些算法只依赖于当前手势之前的相关指针事件。用户代理有责任确保其具体的预测算法实现不会依赖任何额外数据——例如用户在不同站点上的完整交互历史——这些数据可能泄露用户的敏感信息或被用于“指纹识别”与追踪。

除上述注意事项外,工作组认为本规范:

13. 术语表

本节为非规范性内容。

active buttons state
指针的 buttons 属性为非零值的状态。对鼠标而言,即设备至少有一个按键被按下。对触摸而言,即与数字化仪有物理接触。对触控笔而言,即触控笔与数字化仪有物理接触,或在悬停期间至少有一个按钮被按下。
active document
对每一个 active pointer,指其最近一次接收该指针事件的文档。
active pointer
任意可产生事件的触摸接触、触控笔/手写笔、鼠标光标或其他指针。如果某唯一 pointerId 标识的指针仍可能在文档内产生后续事件,则该指针仍被视为活动。示例:
  • 连接到设备的鼠标始终是活动的。
  • 屏幕上的一个触摸接触被视为活动。
  • 如果触摸接触或触控笔离开数字化仪检测范围,则不再被视为活动。
注意
在某些平台,活动指针集合包含设备的全部指针输入,包括任何未指向用户代理的输入(例如指向其他应用的)。
canceled event
其默认行为被 preventDefault()、在事件处理器中返回 false 或其他按 [UIEVENTS] 与 [HTML] 定义方式阻止的事件。
contact geometry
数字化仪上输入(最常见为触摸)接触的边界框。通常用于指针输入分辨率粗于单个像素的设备。部分设备完全不报告该数据。
digitizer
一种输入感应设备,其表面可检测接触和/或近距离输入。最常见的是感应触摸接触或触控笔输入的表面。
direct manipulation
某些用户代理(如触摸屏设备上的浏览器)实现“直接操作”隐喻——指针不仅与控件交互,还直接用于平移或缩放当前页面,提供与内容直接物理接触的错觉。例:触摸屏设备用户通常可用手指或触控笔“抓住”页面并通过移动指针来平移页面,即直接操控页面。对比常规桌面/笔记本上的鼠标指针,平移通常通过滚动条完成,而非“拖拽”页面。
注意
在某些情况下,触摸板(如笔记本上的)允许用户通过在触摸板上“拖动”来滚动。然而这通常通过触摸板生成“伪”鼠标滚轮事件实现,因此不计作直接操作。
hit test
用户代理决定指针事件目标元素的过程。典型地通过考虑指针位置以及文档中元素在屏幕媒介上的视觉布局来确定。
measurable properties

可度量属性表示与连续指针传感器数据相关的值,以实数或来自大域的整数表示。对于指针事件,widthheightpressuretangentialPressuretiltXtiltYtwistaltitudeAngleazimuthAngle 以及 [UIEVENTS] 鼠标事件模型属性 screenXscreenYclientXclientY 都是可度量属性。

相对地,pointerIdpointerTypeisPrimary 以及 [UIEVENTS] 鼠标事件模型属性 buttonbuttonsctrlKeyshiftKeyaltKeymetaKey 不被视为可度量属性,因为它们不与传感器数据相关。

pointer
对能在屏幕上定位到特定坐标(或坐标集合)的输入设备的与硬件无关的抽象表示,例如鼠标、触控笔或触摸接触。

A. 致谢

非常感谢众多人员的提案与建议,其中部分已被采纳进本文档。工作组主席在此感谢以下过去与现任小组成员和参与者的贡献: 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。

B. 修订历史

本节为非规范性内容。

以下为本规范各版本之间的重大与实质性编辑更改的说明性摘要,相对 [PointerEvents2] 规范。参见 本规范编辑草稿的完整修订历史

C. IDL 索引

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;
    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;
    [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;
};

D. 参考文献

D.1 规范性引用

[CSS-OVERFLOW-3]
CSS 溢出模块 第 3 级(CSS Overflow Module Level 3)。Elika Etemad;Florian Rivoal。W3C。7 October 2025。W3C 工作草案。URL: https://www.w3.org/TR/css-overflow-3/
[CSS21]
层叠样式表第 2 级 修订版 1(CSS 2.1)规范。Bert Bos;Tantek Çelik;Ian Hickson;Håkon Wium Lie。W3C。7 June 2011。W3C 推荐。URL: https://www.w3.org/TR/CSS2/
[CSSOM-VIEW]
CSSOM 视图模块(CSSOM View Module)。Simon Fraser; Emilio Cobos Álvarez。W3C。16 September 2025。W3C 工作草案。URL: https://www.w3.org/TR/cssom-view-1/
[DOM]
DOM 标准(DOM Standard)。Anne van Kesteren。WHATWG。 Living Standard。URL: https://dom.spec.whatwg.org/
[ECMASCRIPT]
ECMAScript 语言规范(ECMAScript Language Specification)。 Ecma International。URL: https://tc39.es/ecma262/multipage/
[HTML]
HTML 标准(HTML Standard)。Anne van Kesteren; Domenic Denicola;Dominic Farolino;Ian Hickson;Philip Jägenstedt;Simon Pieters。WHATWG。Living Standard。URL: https://html.spec.whatwg.org/multipage/
[infra]
Infra 标准(Infra Standard)。Anne van Kesteren;Domenic Denicola。WHATWG。Living Standard。URL: https://infra.spec.whatwg.org/
[PointerLock]
指针锁定(Pointer Lock)。Vincent Scheib。W3C。27 October 2016。W3C 推荐。URL: https://www.w3.org/TR/pointerlock/
[RFC2119]
在 RFC 中用于指示需求级别的关键字(Key words for use in RFCs to Indicate Requirement Levels)。S. Bradner。IETF。March 1997。Best Current Practice。URL: https://www.rfc-editor.org/rfc/rfc2119
[RFC8174]
RFC 2119 关键字中大小写的歧义(Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words)。B. Leiba。IETF。May 2017。Best Current Practice。URL: https://www.rfc-editor.org/rfc/rfc8174
[TOUCH-EVENTS]
触摸事件(Touch Events)。Doug Schepers;Sangwhan Moon;Matt Brubeck;Arthur Barstow。W3C。10 October 2013。W3C 推荐。URL: https://www.w3.org/TR/touch-events/
[UIEVENTS]
UI 事件(UI Events)。Gary Kacmarcik;Travis Leithead。W3C。7 September 2024。W3C 工作草案。URL: https://www.w3.org/TR/uievents/
[WEBIDL]
Web IDL 标准(Web IDL Standard)。Edgar Chen;Timothy Gu。 WHATWG。Living Standard。URL: https://webidl.spec.whatwg.org/

D.2 参考性引用

[COMPAT]
兼容性标准(Compatibility Standard)。Mike Taylor。 WHATWG。Living Standard。URL: https://compat.spec.whatwg.org/
[PointerEvents]
指针事件(Pointer Events)。Jacob Rossi;Matt Brubeck。W3C。4 April 2019。W3C 推荐。URL: https://www.w3.org/TR/pointerevents/
[PointerEvents2]
指针事件(Pointer Events)。Matt Brubeck;Rick Byers;Patrick Lauke;Navid Zolghadr。W3C。4 April 2019。W3C 推荐。URL: https://www.w3.org/TR/pointerevents2/
[WCAG22]
网页内容无障碍指南(WCAG)2.2。Michael Cooper;Andrew Kirkpatrick;Alastair Campbell;Rachael Bradley Montgomery;Charles Adams。W3C。12 December 2024。W3C 推荐。URL: https://www.w3.org/TR/WCAG22/