指针事件

第3级

W3C 候选推荐草案

关于本文件的更多详情
本版本:
https://www.w3.org/TR/2026/CRD-pointerevents3-20260522/
最新已发布版本:
https://www.w3.org/TR/pointerevents3/
历史记录:
https://www.w3.org/standards/history/pointerevents3/
提交历史
测试套件:
https://wpt.fyi/pointerevents/
实现报告:
https://w3c.github.io/pointereventswg/implementation-reports/wpt-pointerevents-2026-05-06.html
最新推荐标准:
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 的本次修订旨在取代 [PointerEvents2]。

本文档由 Pointer Events 工作组作为 候选推荐草案发布,并使用 推荐 轨道

作为 候选推荐发布并不意味着 W3C 及其成员的认可。候选推荐草案 集成了 工作组打算纳入 后续候选推荐快照的、相对于先前候选推荐的更改。

这是一份草案文档,可能随时被其他 文档更新、替换或废弃。除作为进行中的工作外,不宜以其他方式引用此文档。

本文档由一个 遵循 W3C 专利 政策运作的工作组制作。 W3C 维护着一份 与该组可交付成果相关的任何专利 披露的公开列表; 该页面还包括 披露专利的说明。实际知晓某项专利且认为该专利包含 基本权利要求的个人, 必须按照 W3C 专利政策第 6 节披露该信息。

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

1. 简介

本节为非规范性内容。

如今,大多数 [HTML] 内容都与鼠标输入一起使用和/或专为鼠标输入而设计。那些 以自定义方式处理输入的内容通常会针对 [POINTEREVENTS4] 鼠标事件编写代码。然而, 如今较新的计算设备还结合了其他形式的输入,包括触摸屏和笔输入。 已有人提出事件类型来分别处理这些输入形式。然而,在为 新输入类型添加支持时,这种方法通常会产生不必要的逻辑重复和事件处理开销。 当内容只考虑一种设备类型来编写时,这通常会产生兼容性问题。此外,为了与现有的基于鼠标的内容兼容,大多数用户代理会为所有输入类型触发鼠标事件。 这使得鼠标事件究竟表示实际的鼠标设备,还是为了兼容性而由 另一种输入类型产生变得含糊不清,从而很难同时针对这两种设备类型编写代码。

为了降低针对多种输入类型编写代码的成本,并帮助解决上述鼠标事件的歧义, 本规范定义了一种更抽象的输入形式,称为指针。指针可以是由鼠标光标、笔、触摸(包括多点触摸)或其他指向 输入设备在屏幕上形成的任何接触点。无论用户拥有什么 硬件,此模型都使编写运行良好的站点和应用程序变得更容易。对于需要设备特定处理的场景,本规范还 定义了用于检查产生该事件的设备类型的属性。主要目标是提供 一组单一的事件和接口,以便更容易地编写跨设备指针输入, 同时仍然只在增强体验有必要时允许设备特定处理。

另一个关键目标是让多线程用户代理能够处理用于平移和缩放的直接操控 操作(例如,在触摸屏上使用手指或触控笔),而不阻塞 脚本执行。

虽然本规范为各种指针输入定义了统一的事件模型,但此模型 不涵盖其他形式的输入,例如键盘或类似键盘的接口(例如, 在仅支持触摸屏的设备上运行的屏幕阅读器或类似辅助技术,其允许 用户按顺序在可聚焦控件和元素之间导航)。虽然用户代理可能会选择 也响应这些接口生成指针事件,但此场景不在 本规范涵盖范围内。

首先,鼓励作者通过响应诸如 focusblurclick 等高级事件,为所有形式的 输入提供等效功能。然而,在使用低级事件(如指针事件)时,鼓励作者 确保支持所有类型的输入。对于键盘和 类似键盘的接口,这可能需要添加显式的键盘事件处理。有关更多 详细信息,请参见 键盘可访问 [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 接口的构造函数使用,以提供一种构造 不受信任的(合成的)指针事件的机制。它继承自现在在 [POINTEREVENTS4] 中定义的 MouseEventInit 字典。有关演示如何触发 不受信任的指针事件的示例代码,请参见示例

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

PointerEvent 接口继承自 MouseEvent, 该接口定义于 Pointer Events。 还请注意 CSSOM View Module 中提出的扩展,该扩展将 各种坐标属性从 long 更改为 double,以允许小数坐标。对于已经为 PointerEvent 实现此拟议扩展、但为常规 MouseEvent 实现此扩展的用户代理,在涉及 clickauxclickcontextmenu 事件时, 还有额外要求。

4.1.1 按钮状态

4.1.1.1 组合按钮交互

某些指针设备(如鼠标或笔)支持多个按钮。在 [POINTEREVENTS4] 鼠标事件模型中,每次按钮按下 都会产生一个 mousedownmouseup 事件。为了更好地抽象这种 硬件差异并简化跨设备输入的编写,Pointer Events 不会为组合按钮按下 (当指针设备上的另一个按钮已经被 按下时再按下附加按钮)触发重叠的 pointerdownpointerup 事件。

相反,可以通过检查 buttonbuttons 属性的变化来检测组合按钮按下。buttonbuttons 属性 继承自 MouseEvent 接口,但其语义和值发生了变化,如以下小节所述。

buttonbuttons 属性的修改仅适用于 pointer events。不过,对于 clickauxclickcontextmenubuttonbuttons 的值必须遵循 [POINTEREVENTS4],就像 兼容性鼠标事件的情况一样。

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 设置指针捕获为目标元素。

在触发此事件之前,用户代理应该 为了确保事件顺序 [POINTEREVENTS4],将目标视为指针设备已从 previousTarget 移动到其上。如果设置了 needsOverEvent 标志,则即使目标 元素相同,也需要一个 pointerover 事件。

向确定的目标触发事件。

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

使用指针捕获 目标覆盖作为目标,而不是正常的命中测试结果,可能会触发一些 边界事件,如 [POINTEREVENTS4] 所定义。这 等同于指针离开其先前目标并进入这个新的捕获目标。当 捕获被释放时,同样的场景可能发生,因为指针正在离开捕获 目标并进入命中测试目标。
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。

许多用户代理在 MouseEvents 中公开非标准属性 fromElementtoElement 以支持旧版内容。我们鼓励这些用户代理将 PointerEvents 中这些(继承的)属性的值 设置为 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 不冒泡,并且其 分派甚至会考虑后代元素的命中测试边界。
此事件类型、[POINTEREVENTS4] 中描述的 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,以及从 [POINTEREVENTS4] 继承的坐标。pointercancel 事件中的 coalescedEventspredictedEvents 列表必须为空,并且该事件的 cancelable 属性 必须为 false。

4.2.8 pointerout 事件

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

4.2.9 pointerleave 事件

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

此事件类型类似于 pointerout,但有两个 不同之处:pointerleave 不冒泡,并且其 分派甚至会考虑后代元素的命中测试边界。
此事件类型、[POINTEREVENTS4] 中描述的 mouseleave 事件, 以及 [CSS21] 中描述的 CSS :hover 伪类之间存在相似之处。 另请参见 pointerenter 事件。

4.2.10 gotpointercapture 事件

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

4.2.11 lostpointercapture 事件

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

4.2.12 clickauxclickcontextmenu 事件

本节是对 [POINTEREVENTS4] 中定义的 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 实现了 CSSOM View Module 中拟议 更改的用户代理,必须 使用 Math.floor [ECMASCRIPT],将 clickauxclickcontextmenu 的各种坐标属性转换为 long 值(如原始 UI Events 中所定义)。

4.2.12.3 事件分派

clickauxclickcontextmenu 事件必须遵循 [POINTEREVENTS4] 规范中定义的分派过程,除了事件目标会 使用以下算法进行覆盖:

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

    事件 userEvent 可能是非 PointerEvent;例如, 当 click 事件分派是由在 checkbox 元素上按下空格键导致时, 它是一个 KeyboardEvent

    userEventPointerEvent 时,对于 clickauxclick 事件,userEvent 是 一个 pointerup;而对于 contextmenu 事件,它是一个 pointerdownpointerup 事件(取决于 原生平台约定)。

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

    如果 eventcontextmenu 事件,或者 userEvent 是在 对应指针被捕获期间分派的,则令 targetuserEvent 的目标。

    否则(eventclickauxclick 事件,且 userEvent 是一个在未被捕获状态下分派的 pointerup 事件), 令 target 为在 event 正被分派的时刻,DOM 中对应 pointerdownpointerup 目标的最近共同包含祖先。

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

    如果 userEvent 被捕获,则 event 会被分派到 userEvent 的捕获目标,即使具有相同 pointerIdlostpointercapture 事件 已经被分派。

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 不会应用/级联到嵌入式浏览上下文。 例如,即使将 touch-action 应用于 <iframe>,也不会 对 <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在实现中很常见,定义于 [COMPAT]。
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 与任何活跃指针都不匹配,则抛出一个 "NotFoundError" DOMException
  2. pointer 为给定 pointerId 所指定的活跃指针
  3. 如果 element 不是已连接的 [DOM],则抛出一个 "InvalidStateError" DOMException
  4. 如果在 element节点文档 [DOM] 具有锁定元素([PointerLock] pointerLockElement)时调用此方法, 则抛出一个 "InvalidStateError" DOMException
  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应该模拟单个 传统鼠标输入的移动。这意味着每个事件目标的进入/离开状态都是有效的,并符合 [POINTEREVENTS4]。用户代理应该 通过按如下方式维护文档中传统鼠标指针的有效位置来保证这一点。

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

  1. T 为正在分派的 pointerdownpointeruppointermove 事件的目标。对于 pointerleave 事件,取消设置 T
  2. 如果 T 和当前传统鼠标指针的有效 位置均未设置,或者它们相等,则终止这些步骤。
  3. 按照 [POINTEREVENTS4],为从当前传统 鼠标指针的有效位置移动到 T 的鼠标分派 mouseovermouseoutmouseentermouseleave 事件。将当前传统鼠标指针的有效 位置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. 术语表

本节为非规范性内容。

活跃按钮状态
指针的 buttons 属性具有非零值时的状态。对于鼠标,这 是指设备至少有一个按钮被按下。对于触摸,这是指与数字化器存在物理接触。 对于笔,这是指笔与数字化器存在物理接触,或者在悬停时至少有一个按钮被按下。
活跃文档
对于每个活跃指针,接收来自 该指针的最后一个事件的文档。
活跃指针
任何可以产生事件的触摸接触、笔/触控笔、鼠标光标或其他指针。如果给定指针 (由唯一的 pointerId 标识) 仍可能在文档内产生其他事件,则该指针仍被视为活跃。示例:
  • 连接到设备的鼠标始终处于活跃状态。
  • 屏幕上的触摸接触被视为活跃。
  • 如果触摸接触或笔/触控笔被抬起并超出数字化器的范围,则它不再 被视为活跃。
在某些平台上,活跃指针集合包括设备上的所有指针输入, 包括任何并非以用户代理为目标的输入(例如以其他 应用程序为目标的输入)。
已取消事件
其默认动作通过 preventDefault()、在事件处理器中返回 false,或通过 [UIEVENTS] 和 [HTML] 定义的其他方式被阻止的事件。
接触几何
数字化器上输入(最常见的是触摸)的边界框。这通常指具有 比单个像素更粗略的指针输入分辨率的设备。有些设备根本不报告此数据。
数字化器
一种输入感测设备,其表面可以检测与其接触和/或与其非常 接近的输入。最常见的是,这种表面感测来自触摸接触或笔/触控笔的输入。
直接操控
某些用户代理(例如触摸屏设备上的浏览器)实现了一种“直接操控” 隐喻,其中指针不仅与控件交互, 还用于直接平移或缩放当前页面,从而提供直接物理 接触的错觉。例如,触摸屏设备上的用户通常 能够使用手指或触控笔“抓住”页面,并通过移动指针来平移页面,从而直接 操控页面。相比之下,在普通 台式机/笔记本电脑上的鼠标指针中,平移是通过使用滚动条完成的,而不是通过“拖动”页面完成的。
在某些情况下,触控板(如笔记本电脑上的触控板)将允许用户通过在触控板上“拖动” 来滚动。然而,这通常是通过触控板生成“假的”鼠标滚轮事件实现的, 因此这不算作直接 操控。
命中 测试
用户代理确定指针事件的目标元素的过程。通常,这是 通过考虑指针的位置以及屏幕媒体中文档内元素的视觉布局来确定的。
可测量属性

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

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

指针
输入设备的硬件无关表示,这些输入设备可以指向屏幕上的特定坐标 (或一组坐标),例如鼠标、笔或触摸接触。

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 Overflow Module Level 3. Elika Etemad; Florian Rivoal. W3C. 2025 年 10 月 7 日。W3C 工作草案。URL: https://www.w3.org/TR/css-overflow-3/
[CSS21]
Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification. Bert Bos; Tantek Çelik; Ian Hickson; Håkon Wium Lie. W3C. 2011 年 6 月 7 日。W3C 推荐标准。URL: https://www.w3.org/TR/CSS2/
[CSSOM-VIEW]
CSSOM View Module. Simon Fraser; Emilio Cobos Álvarez. W3C. 2025 年 9 月 16 日。W3C 工作草案。URL: https://www.w3.org/TR/cssom-view-1/
[DOM]
DOM Standard. Anne van Kesteren. WHATWG. 现行标准。URL: https://dom.spec.whatwg.org/
[ECMASCRIPT]
ECMAScript Language Specification. Ecma International. URL: https://tc39.es/ecma262/multipage/
[HTML]
HTML Standard. Anne van Kesteren; Domenic Denicola; Dominic Farolino; Ian Hickson; Philip Jägenstedt; Simon Pieters. WHATWG. 现行 标准。URL: https://html.spec.whatwg.org/multipage/
[infra]
Infra Standard. Anne van Kesteren; Domenic Denicola. WHATWG. 现行标准。URL: https://infra.spec.whatwg.org/
[POINTEREVENTS4]
Pointer Events. Patrick Lauke; Robert Flack. W3C. 2026 年 5 月 22 日。W3C 工作草案。URL: https://www.w3.org/TR/pointerevents4/
[PointerLock]
Pointer Lock 2.0. Mustaq Ahmed; Vincent Scheib. W3C. 2026 年 2 月 25 日。W3C 工作草案。URL: https://www.w3.org/TR/pointerlock-2/
[RFC2119]
Key words for use in RFCs to Indicate Requirement Levels. S. Bradner. IETF. 1997 年 3 月。当前最佳实践。URL: https://www.rfc-editor.org/rfc/rfc2119
[RFC8174]
Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words. B. Leiba. IETF. 2017 年 5 月。当前最佳实践。URL: https://www.rfc-editor.org/rfc/rfc8174
[TOUCH-EVENTS]
Touch Events. Doug Schepers; Sangwhan Moon; Matt Brubeck; Arthur Barstow. W3C. 2013 年 10 月 10 日。W3C 推荐标准。URL: https://www.w3.org/TR/touch-events/
[UIEVENTS]
UI Events. Xiaoqian Wu. W3C. 2026 年 2 月 21 日。W3C 工作草案。URL: https://www.w3.org/TR/uievents/
[WEBIDL]
Web IDL Standard. Edgar Chen; Timothy Gu. WHATWG. 现行标准。URL: https://webidl.spec.whatwg.org/

D.2 资料性引用

[COMPAT]
Compatibility Standard. Mike Taylor. WHATWG. 现行标准。URL: https://compat.spec.whatwg.org/
[PointerEvents]
Pointer Events. Patrick Lauke; Robert Flack. W3C. 2026 年 5 月 22 日。W3C 工作草案。URL: https://www.w3.org/TR/pointerevents4/
[PointerEvents2]
Pointer Events. Matt Brubeck; Rick Byers; Patrick Lauke; Navid Zolghadr. W3C. 2019 年 4 月 4 日。W3C 推荐标准。URL: https://www.w3.org/TR/pointerevents2/
[WCAG22]
Web Content Accessibility Guidelines (WCAG) 2.2. Michael Cooper; Andrew Kirkpatrick; Alastair Campbell; Rachael Bradley Montgomery; Charles Adams. W3C. 2024 年 12 月 12 日。W3C 推荐标准。URL: https://www.w3.org/TR/WCAG22/