指针事件

第4级

W3C工作草案

关于本文件的更多详情
当前版本:
https://www.w3.org/TR/2026/WD-pointerevents4-20260225/
最新发布版本:
https://www.w3.org/TR/pointerevents4/
最新编辑草案:
https://w3c.github.io/pointerevents/
历史记录:
https://www.w3.org/standards/history/pointerevents4/
提交记录
测试套件:
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 主题行 [pointerevents4] … 消息主题 … (存档)
浏览器支持:
caniuse.com

摘要

Pointer Events 规范定义了一个统一、与硬件无关的输入处理框架,支持来自多种设备的输入,包括鼠标、触摸屏和笔/手写笔。通过提供一套统一的事件(例如 pointerdown,pointermove,pointerup),开发者无需为每种设备单独编写特定逻辑,即可支持多样化的输入方式。

本规范还定义了鼠标和滚轮事件,并规定了为其他指针设备类型触发鼠标事件的映射方式。

本文件状态

本节说明本文档在发布时的状态。当前 W3C 发布列表和本技术报告的最新版本可在 W3C 标准与草案索引 找到。

本规范是对 [PointerEvents3] 规范的更新。同时也包含了此前 [UIEVENTS] 规范中的鼠标和滚轮事件。

此次修订包含了新功能:

本文档由 Pointer Events 工作组 作为工作草案,根据 推荐标准流程 发布。

作为工作草案发布,并不代表 W3C 及其成员的背书。

本文为草稿文档,可能随时被更新、替换或废弃。除作为正在进行的工作外,引用此文档并不合适。

本文档由遵循 W3C 专利政策的工作组编写。 W3C 维护着与本组交付物相关的 专利披露公开列表; 该页面还包含有关专利披露的说明。如果个人实际知晓某专利符合 必须声明的权利要求,必须依据 W3C 专利政策第6节 披露相关信息。

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

1. 简介

本节为非规范性内容。

当前,大多数 [HTML] 内容被用于或设计用于鼠标输入。那些以自定义方式处理输入的内容,通常会针对 [UIEVENTS] 鼠标事件进行编码。然而,现代计算设备正逐步引入其他输入方式,包括触摸屏和笔输入。针对这些不同输入形式已提出专门的事件类型来分别处理,但这种做法在新增某类输入支持时,常常导致逻辑和事件处理的重复,带来不必要的开销。此种方式还会造成内容仅考虑某一种设备类型时的兼容性问题。此外,为了与基于鼠标的现有内容兼容,大多数用户代理会为所有输入类型触发鼠标事件。这就会让鼠标事件是否真正代表鼠标设备变得模糊不清,抑或只是为兼容性由其他输入类型生成的,使得同时针对多种设备编码变得困难。

为减少多输入类型编码的成本,同时缓解上述鼠标事件的歧义问题,本文规范定义了一种更抽象的输入方式,称为指针(pointer)。指针可以是屏幕上由鼠标光标、笔、触摸(包括多点触控)或其他指向性输入设备所产生的任意接触点。这一模型让站点和应用能够更容易地兼容用户设备的各种硬件。对于需要进行设备专属处理的场景,本规范也定义了用于检测产生事件的设备类型的属性。主要目标是提供一套统一的事件及接口,让跨设备指针输入的开发更加便利,同时仅在需要时提供设备特有的增强体验。

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

注意

虽然本规范定义了用于多种指针输入的统一事件模型,但该模型并不涵盖如键盘或类键盘接口的其它输入(如仅有触摸屏的设备上的屏幕阅读器或类似辅助技术,其允许用户依次导航可聚焦的控件和元素)。虽然用户代理可以选择对这些接口也生成指针事件,但这种情形并未包含在本规范中。

首先,建议作者为所有输入方式通过响应高级事件(如 focusblurclick)提供等价功能。但在使用底层事件(如 Pointer Events)时,建议确保支持所有类型的输入。对于键盘及类键盘接口,可能需要额外处理显式的键盘事件。更多信息参见 可键盘操作 [WCAG22]。

Pointer input combines various input sources such as mouse, pen, and touch
1 指针是一种与硬件无关的输入设备抽象,能指向屏幕上的特定坐标(或一组坐标)。

用于处理通用指针输入的事件,与鼠标事件非常相似:pointerdownpointermovepointeruppointeroverpointerout 等。这极大方便了从鼠标事件到指针事件的内容迁移。 Pointer Events 除了包含 Mouse Events 的全部常用属性(如客户端坐标、目标元素、按钮状态)外,还新增了针对不同输入方式的属性,例如压力、接触几何、倾斜角等。这样,作者便可根据需要,灵活地在逻辑复用和设备专项体验间做取舍。

指针事件可能来源于多种输入设备,但其并未被定义为从其它特定设备事件转换生成。尽管可以为兼容性目的这样做,并很鼓励这么做,但此规范并不要求必须实现其它设备相关事件(如鼠标事件或触摸事件)。用户代理可以仅实现指针事件而不实现其它设备事件。为兼容只处理鼠标事件的内容,本规范也提供了可选章节,描述如何根据鼠标之外设备的指针输入生成兼容鼠标事件

注意

本规范未对同时支持 [TOUCH-EVENTS] Touch Events 和 Pointer Events 的用户代理的期望行为提出建议。两者之间关系的详细信息见 Touch Events 社区组

2. 一致性

除标记为非规范性的章节外,本规范中的所有编写指南、图示、示例与注释均为非规范性内容。本规范中其余内容均为规范性内容。

本文档中的关键字 MAYMUSTMUST NOTOPTIONALSHOULD 的解释应遵循 BCP 14 [RFC2119] [RFC8174] 中所述,仅当这些词以全大写形式出现时(如这里所示)才适用。

3. 示例

本节为非规范性内容。

以下是一些基础示例,用于演示作者可能如何使用本规范中的部分 API。此外,在本文档相关章节还提供了更具体的示例。

示例 1: 特性检测与事件绑定
/* Bind to either Pointer Events or traditional touch/mouse */

if (window.PointerEvent) {
    // if Pointer Events are supported, only listen to pointer events
    target.addEventListener("pointerdown", function(e) {
        // if necessary, apply separate logic based on e.pointerType
        // for different touch/pen/mouse behavior
        ...
    });
    ...
} else {
    // traditional touch/mouse event handlers
    target.addEventListener('touchstart', function(e) {
        // prevent compatibility mouse events and click
        e.preventDefault();
        ...
    });
    ...
    target.addEventListener('mousedown', ...);
    ...
}

// additional event listeners for keyboard handling
...
示例 2: 检测用户的输入类型
window.addEventListener("pointerdown", detectInputType);

function detectInputType(event) {
    switch(event.pointerType) {
        case "mouse":
            /* mouse input detected */
            break;
        case "pen":
            /* pen/stylus input detected */
            break;
        case "touch":
            /* touch input detected */
            break;
        default:
            /* pointerType is empty (could not be detected)
            or UA-specific custom type */
    }
}
示例 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);
示例 5: 在 PointerDown 时分配笔颜色
<div style="position:absolute; top:0px; left:0px; width:100px;height:100px;"></div>
<script>
window.addEventListener("pointerdown", assignPenColor);
window.addEventListener("pointermove", assignPenColor);
const colorMap = new Map();

function assignPenColor(event) {
    const uniqueId = event.persistentDeviceId;
    // Check if a unique Id exists.
    if (uniqueId == 0) {
        return;
    }
    // Check if a color has been assigned to the device.
    if (map.has(uniqueId)) {
        return;
    }
    // Assign a color to the device.
    let newColor = getNewColor();
    map.set(uniqueId, newColor);
    return newColor;
}

function getNewColor() {
    /* return some color value */
}
</script>

4. 鼠标事件

鼠标事件模块起源于 [HTML401] 的 onclickondblclickonmousedownonmouseuponmouseoveronmousemoveonmouseout 属性。该事件模块专门为指向性输入设备(如鼠标或轨迹球)而设计。

4.1 MouseEvent 接口

在 DOM Level 2 中引入,并在本规范中进行了修改。

MouseEvent 接口提供与鼠标事件相关的特定上下文信息。

在嵌套元素的情况下,鼠标事件始终以最深层嵌套的元素为目标。

注意

目标元素的祖先元素可以使用事件冒泡来获取其后代元素内发生的鼠标事件通知。

要创建 MouseEvent 接口的实例,请使用 MouseEvent 构造函数,并传入一个可选的 MouseEventInit 字典。

注意

在使用 initMouseEvent 初始化 MouseEvent 对象时,实现可以使用客户端坐标 clientXclientY 来计算其它坐标(例如 DOM Level 0 实现所暴露的目标坐标,或其它专有属性,如 pageX)。

4.1.1 MouseEvent

WebIDL[Exposed=Window]
interface MouseEvent : UIEvent {
	constructor(DOMString type, optional MouseEventInit eventInitDict = {});
	readonly attribute long screenX;
	readonly attribute long screenY;
	readonly attribute long clientX;
	readonly attribute long clientY;
	readonly attribute long layerX;
	readonly attribute long layerY;

	readonly attribute boolean ctrlKey;
	readonly attribute boolean shiftKey;
	readonly attribute boolean altKey;
	readonly attribute boolean metaKey;

	readonly attribute short button;
	readonly attribute unsigned short buttons;

	readonly attribute EventTarget? relatedTarget;

	boolean getModifierState(DOMString keyArg);
};
screenX

事件发生时相对于屏幕坐标系原点的水平坐标。

该属性的未初始化值 MUST0

screenY

事件发生时相对于屏幕坐标系原点的垂直坐标。

该属性的未初始化值 MUST0

clientX

事件发生时相对于与该事件相关联的视口的水平坐标。

该属性的未初始化值 MUST0

clientY

事件发生时相对于与该事件相关联的视口的垂直坐标。

该属性的未初始化值 MUST0

layerX

相对于最近的祖先元素的水平偏移量;该祖先元素是堆叠上下文,是定位的,或在绘制堆叠上下文时在定位阶段进行绘制。

该属性的未初始化值 MUST0

layerY

相对于最近的祖先元素的垂直偏移量;该祖先元素是堆叠上下文,是定位的,或在绘制堆叠上下文时在定位阶段进行绘制。

该属性的未初始化值 MUST0

ctrlKey

参见 KeyboardEventctrlKey 属性。

该属性的未初始化值 MUSTfalse

shiftKey

参见 KeyboardEventshiftKey 属性。

该属性的未初始化值 MUSTfalse

altKey

参见 KeyboardEventaltKey 属性。

该属性的未初始化值 MUSTfalse

metaKey

参见 KeyboardEventmetaKey 属性。

该属性的未初始化值 MUSTfalse

button

在由鼠标按钮按下或释放引起的鼠标事件期间,button MUST 用于指示哪个指针设备按钮发生了状态变化。

button 属性的取值 MUST 如下:

  • 0 MUST 表示设备的主按钮(通常为左键,或单按钮设备上的唯一按钮,用于激活用户界面控件或选择文本)或未初始化值。
  • 1 MUST 表示辅助按钮(通常为中键,常与鼠标滚轮组合)。
  • 2 MUST 表示次按钮(通常为右键,常用于显示上下文菜单)。
  • 3 MUST 表示 X1(后退)按钮。
  • 4 MUST 表示 X2(前进)按钮。

某些指向设备提供或模拟更多按钮状态,可使用大于 2 或小于 0 的值来表示这些按钮。

注意

对于非由鼠标按钮按下/释放导致的事件,button 的值不会更新。在这些场景中,应注意不要将 0 解释为左键,而应将其视为默认值。

注意

mousedownmouseup 等事件相关的某些默认操作,取决于所使用的具体鼠标按钮。

该属性的未初始化值 MUST0

buttons

在任何鼠标事件期间,buttons MUST 用于指示当前被按下的鼠标按钮组合,并以位掩码(bitmask)表示。

注意

尽管名称相似,buttons 属性与 button 属性的取值差异很大。button 的值被假定在 mousedown / mouseup 事件处理程序中有效;而 buttons 属性反映任意受信任的 MouseEvent 对象(在其被派发期间)中鼠标按钮的状态,因为它可以表示“当前没有任何按钮处于激活状态”的状态(0)。

buttons 属性的取值 MUST 如下:

  • 0 MUST 表示当前没有按钮处于激活状态。
  • 1 MUST 表示设备的主按钮(通常为左键,或单按钮设备上的唯一按钮,用于激活用户界面控件或选择文本)。
  • 2 MUST 表示次按钮(通常为右键,常用于显示上下文菜单),如果存在。
  • 4 MUST 表示辅助按钮(通常为中键,常与鼠标滚轮组合)。

某些指向设备提供或模拟更多按钮。为表示这些按钮,每增加一个后续按钮,其值 MUST 都必须加倍(按二进制序列 81632、...)。

注意

由于任意一组按钮值之和都是唯一的数字,内容作者可以使用按位运算来确定当前按下了多少个按钮以及分别是哪些按钮,且可适用于设备上任意数量的鼠标按钮。例如,值 3 表示左键与右键当前都被按下;值 5 表示左键与中键当前都被按下。

注意

mousedownmouseup 等事件相关的某些默认操作,取决于所使用的具体鼠标按钮。

该属性的未初始化值 MUST0

relatedTarget

用于根据事件类型,标识与某个 UI 事件相关的次要 EventTarget

该属性的未初始化值 MUSTnull

getModifierState(keyArg)

使用一个键值来查询某个修饰键的状态。

若为修饰键且该修饰键已激活则返回 true,否则返回 false

DOMString keyArg
关于此参数的说明,参见 KeyboardEventgetModifierState() 方法。

4.1.2 MouseEventInit

WebIDLdictionary MouseEventInit : EventModifierInit {
	long screenX = 0;
	long screenY = 0;
	long clientX = 0;
	long clientY = 0;

	short button = 0;
	unsigned short buttons = 0;
	EventTarget? relatedTarget = null;
};
screenX

screenX 属性初始化为所需的、鼠标指针在用户屏幕上的水平相对位置。

将事件对象初始化为给定的鼠标位置,不得将用户的鼠标指针移动到所初始化的位置。

screenY

screenY 属性初始化为所需的、鼠标指针在用户屏幕上的垂直相对位置。

将事件对象初始化为给定的鼠标位置,不得将用户的鼠标指针移动到所初始化的位置。

clientX

clientX 属性初始化为所需的、鼠标指针相对于用户浏览器客户端窗口的水平位置。

将事件对象初始化为给定的鼠标位置,不得将用户的鼠标指针移动到所初始化的位置。

clientY

clientY 属性初始化为所需的、鼠标指针相对于用户浏览器客户端窗口的垂直位置。

将事件对象初始化为给定的鼠标位置,不得将用户的鼠标指针移动到所初始化的位置。

button

button 属性初始化为一个数字,用于表示鼠标按钮的期望状态。

注意

值 0 用于表示主鼠标按钮,值 1 用于表示辅助/中键,值 2 用于表示右键。大于 2 的数字也可能出现,但本文档未对此进行规定。

buttons

buttons 属性初始化为一个数字,用于表示鼠标按钮中需要被视为激活的一个或多个按钮。

注意

buttons 属性是一个位字段(bit-field)。若对位字段值应用掩码 1 后结果为真,则主鼠标按钮处于按下状态;若应用掩码 2 后结果为真,则右键处于按下状态;若应用掩码 4 后结果为真,则辅助/中键处于按下状态。

在 JavaScript 中,要将 buttons 属性初始化为如同右键(2)与中键(4)同时被按下,可将 buttons 的值赋为以下任一形式:

{ buttons: 2 | 4 }

或:

{ buttons: 6 }
relatedTarget

relatedTarget 应初始化为鼠标指针刚刚离开的元素边界(对于 mouseovermouseenter 事件),或鼠标指针正在进入其边界的元素(对于 mouseoutmouseleavefocusout 事件)。对于其它事件,该值无需赋值(默认为 null)。

实现 MUST 在生成鼠标事件时维护 当前点击计数。该计数 MUST 是一个非负整数,用于表示在特定时间内指向设备按钮的连续点击次数。计数在何时重置的延迟取决于环境配置。

4.2 MouseEvent 算法

4.2.1 原生操作系统要求

本节中的算法假设原生平台操作系统将提供以下内容:

  • 当鼠标移动时触发的事件(由 处理 原生鼠标移动 处理)
  • 当鼠标按钮按下时触发的事件(由 处理 原生鼠标按下 处理)
  • 当鼠标按钮释放时触发的事件(由 处理 原生鼠标释放 处理)
  • 一种用于识别鼠标按钮按下何时应被解释为“点击”的方式(由 处理原生鼠标点击 处理)
    • 例如,作为一个标志位,或作为一个单独事件
    • 如果触发一个单独的“点击”事件,则原生操作系统将紧接对应的“鼠标释放”事件之后立即触发它,且中间不会插入其他与鼠标相关的事件
  • 一种用于识别鼠标单击何时为“双击”的方式(由 处理原生鼠标双击 处理)

对于这些事件,操作系统将能够提供以下信息:

  • 相对于原生操作系统桌面的 x,y 鼠标坐标
  • 相对于用户代理窗口视口的 x,y 鼠标坐标
  • 当前被按住的键盘修饰键

4.2.2 构造鼠标事件

警告

本节需要修订。

一般而言,当调用 Event 接口的构造函数,或调用继承自 Event 接口的接口构造函数时,应遵循 [DOM] 中描述的步骤。然而,MouseEvent 接口还提供了额外的字典成员,用于初始化 Event 对象的按键修饰符的内部状态:具体来说,即通过 getModifierState() 方法查询的内部状态。本节补充了 [DOM] 中初始化新的 MouseEvent 对象的步骤,以包含这些可选的修饰键状态。

为了构造 MouseEvent,或使用下述算法从这些对象派生的对象,所有 MouseEvent 及其派生对象都具有 内部按键修饰符状态,其可使用 [UIEvents-Key] 中 修饰键表 所描述的 修饰键名称 来进行设置与读取。

以下步骤补充了 [DOM] 中定义的用于构造事件的算法:

4.2.3 MouseEvent 的全局状态

警告

本节需要修订。

4.2.3.1 用户代理层级状态

用户代理必须维护以下在整个用户代理范围内共享的值。

一个用于跟踪鼠标按钮当前状态的 鼠标按钮位掩码

4.2.3.2 窗口层级状态

用户代理必须维护以下在该窗口范围内共享的值。

一个 上一个鼠标元素 值(初始为 undefined),用于跟踪我们最后一次向其发送 MouseEvent 的 Element

一个 上一个鼠标 DOM 路径 值(初始为空),包含最近一次发送鼠标事件时 上一个鼠标元素 的祖先 Element 的快照。

4.2.4 MouseEvent 的内部状态

警告

本节需要修订。

一个 MouseEvent 具有以下内部标志位,用于跟踪各类修饰键的状态: shift 标志control 标志alt 标志altgraph 标志、 以及 meta 标志。 若对应的修饰键在鼠标事件发生时处于按下状态,则设置这些标志位。

4.2.5 命中测试

警告

本节需要修订。

  1. pos 为相对于视口的 x,y 坐标
  2. 返回 [CSSOM-View] 的 elementFromPoint() 并传入 pos(位于 pos 的最前层 DOM 元素)
    注意

    为考虑 inertdisabled 元素,这里应调用 elementsFromPoint() 并拒绝无效元素。

4.2.6 初始化 MouseEvent

警告

本节需要修订。

要使用 eventeventTypeeventTargetbubblescancelable初始化一个 MouseEvent,请运行以下步骤:

  1. 使用 eventeventTypeeventTargetbubblescancelable初始化一个 UIEvent
  2. event.screenX 设为事件发生位置相对于桌面原点的 x 坐标
  3. event.screenY 设为事件发生位置相对于桌面原点的 y 坐标
  4. event.clientX 设为事件发生位置相对于 视口 原点的 x 坐标
  5. event.clientY 设为事件发生位置相对于 视口 原点的 y 坐标
  6. 使用 event设置鼠标事件修饰符
  7. event.button 设为 0
  8. event.buttons 设为 鼠标按钮位掩码
  9. 使用 event初始化 MouseEvent 的 PointerLock 属性
    问题 1

    我们应该为 PointerLock 提供一个钩子,而不是在此处将其硬编码。

4.2.7 设置鼠标事件修饰符

警告

本节需要修订。

  1. event 为要更新的 MouseEvent
  2. 如果 按键修饰符状态 包含 "Shift",则设置 eventshift 标志,否则取消设置
  3. 如果 按键修饰符状态 包含 "Control",则设置 eventcontrol 标志,否则取消设置
  4. 如果 按键修饰符状态 包含 "Alt",则设置 eventalt 标志,否则取消设置
  5. 如果 按键修饰符状态 包含 "AltGraph",则设置 eventaltgraph 标志,否则取消设置
  6. 如果 按键修饰符状态 包含 "Meta",则设置 eventmeta 标志,否则取消设置
  7. 若事件的 shift 标志 已设置,则将 event.shiftKey 设为 true,否则设为 false
  8. 若事件的 control 标志 已设置,则将 event.ctrlKey 设为 true,否则设为 false
  9. 若事件的 alt 标志altgraph 标志 已设置,则将 event.altKey 设为 true,否则设为 false
  10. 若事件的 meta 标志 已设置,则将 event.metaKey 设为 true,否则设为 false

4.2.8 创建可取消的 MouseEvent

警告

本节需要修订。

  1. eventType 为包含有效 MouseEvent 类型的 DOMString
  2. eventTarget 为该事件的 EventTarget
  3. bubbles 为 true
  4. cancelable 为 true
  5. event 为使用 MouseEvent 进行创建事件的结果
  6. 使用 eventeventTypeeventTargetbubblescancelable初始化一个 MouseEvent
  7. 返回 event

4.2.9 创建不可取消的 MouseEvent

警告

本节需要修订。

  1. eventType 为包含有效 MouseEvent 类型的 DOMString
  2. eventTarget 为该事件的 EventTarget
  3. bubbles 为 "false"
  4. cancelable 为 "false"
  5. event 为使用 MouseEvent 进行创建事件的结果
  6. 使用 eventeventTypeeventTargetbubblescancelable初始化一个 MouseEvent
  7. 返回 event

4.2.10 计算 MouseEvent 的 button 属性

警告

本节需要修订。

这将返回一个适用于存储到 MouseEventbutton 属性中的按钮 ID。

  1. mbutton 为用于标识某个鼠标按钮的 ID
  2. 如果 mbutton 为主鼠标按钮,则返回 0
  3. 如果 mbutton 为辅助(中键)鼠标按钮,则返回 1
  4. 如果 mbutton 为次鼠标按钮,则返回 2
  5. 如果 mbutton 为 X1(后退)按钮,则返回 3
  6. 如果 mbutton 为 X2(前进)按钮,则返回 4

4.2.11 从原生事件设置 MouseEvent 属性

警告

本节需要修订。

  1. event 为要初始化的 MouseEvent
  2. native 为原生鼠标事件
    编辑者注

    TODO.

  3. 如果 event.type 属于 [ mousedown, mouseup ],则
    1. mbutton 为从 native 获取的、用于标识被按下的鼠标按钮的 ID
    2. event.button 设为 使用 mbutton 运行 计算 MouseEvent 的 button 属性 的结果

4.2.12 处理原生鼠标按下

警告

本节需要修订。

  1. native 为原生 mousedown
  2. mbutton 为从 native 获取的、用于标识被按下的鼠标按钮的 ID
  3. 按以下方式更新 鼠标按钮位掩码
    1. 如果 mbutton 为主鼠标按钮,则设置 0x01 位
    2. 如果 mbutton 为次鼠标按钮,则设置 0x02 位
    3. 如果 mbutton 为辅助(中键)鼠标按钮,则设置 0x04 位
      注意

      其他按钮可从 0x08 开始添加。

  4. target 为使用从 native 获取的相对于视口的坐标来运行 命中测试 的结果
  5. event 为使用 "mousedown" 与 target 运行 创建可取消的 MouseEvent 的结果
  6. 使用 native 运行 从原生事件设置 MouseEvent 属性
  7. 使用 event 运行 可能发送 pointerdown 事件
  8. result 为在 target 处对 event 执行 派发 的结果
  9. result 为 true 且 target 是一个可聚焦区域可通过点击聚焦,则
    1. target 处运行聚焦步骤
  10. mbutton 为次鼠标按钮,则
    1. 使用 nativetarget 运行 可能显示上下文菜单

4.2.13 处理原生鼠标释放

警告

本节需要修订。

  1. native 为原生 mouseup
    注意

    在 mousedown 与 mouseup 之间可能发生其他鼠标事件。

  2. mbutton 为从 native 获取的、用于标识被按下的鼠标按钮的 ID
  3. 按以下方式更新 鼠标按钮位掩码
    1. 如果 mbutton 为主鼠标按钮,则清除 0x01 位
    2. 如果 mbutton 为次鼠标按钮,则清除 0x02 位
    3. 如果 mbutton 为辅助(中键)鼠标按钮,则清除 0x04 位
  4. target 为使用从 native 获取的相对于视口的坐标来运行 命中测试 的结果
  5. event 为使用 "mouseup" 与 target 运行 创建可取消的 MouseEvent 的结果
  6. 使用 native 运行 从原生事件设置 MouseEvent 属性
  7. 使用 event 运行 可能发送 pointerup 事件
  8. target 处对 event 执行 派发

4.2.14 处理原生鼠标点击

警告

本节需要修订。

  1. native 为原生鼠标点击
    注意

    对于会生成点击的 mouseup,平台应在 处理原生鼠标释放 之后立即调用此算法。

  2. target 为使用从 native 获取的相对于视口的坐标来运行 命中测试 的结果
  3. 使用 nativetarget 运行 发送点击事件

4.2.15 发送点击事件

警告

本节需要修订。

  1. native 为原生 mousedown
  2. target 为该事件的 EventTarget
  3. mbutton 为 1(默认主鼠标按钮)
  4. native 有效,则
    1. mbutton 为从 native 获取的、用于标识被按下鼠标按钮的 ID
  5. mbutton 为主鼠标按钮,则将 eventType 设为 "click",否则设为 "auxclick"
  6. event 为使用 eventTypetarget 运行 创建一个 PointerEvent 的结果
  7. native 有效,则
    1. 使用 eventnative 运行 从原生事件设置 MouseEvent 属性
    2. event.screenX 不是整数值,则将其舍入。
    3. event.screenY 不是整数值,则将其舍入。
  8. target 处对 event 执行 派发
    注意

    关于浏览器使用 PointerEvents 与坐标舍入的信息,见 pointerevents/100

    编辑者注

    任何“默认操作”都会在派发期间通过触发目标的 激活行为 算法来处理。因此无需在这里处理。 但是,需要验证现有规范是否处理了 disabled/css-pointer-events/inert/...

    注意

    为处理 HTMLelement.click(),请以 native = null 且 target = HTMLelement 调用此算法。

    注意

    为处理由键盘发起的点击,请以 native = null 且 target = 当前聚焦元素 调用此算法。

4.2.16 处理原生鼠标双击

警告

本节需要修订。

  1. native 为原生鼠标双击
    注意

    对于会生成双击的鼠标点击,本算法应在处理原生鼠标点击之后立即被调用。

  2. mbutton 为从 native 获取的、用于标识被按下鼠标按钮的 ID
  3. mbutton 不是主鼠标按钮,则返回
  4. target 为使用从 native 获取的相对于视口的坐标来运行 命中测试 的结果
  5. event 为使用 "dblclick" 与 target 运行 创建一个 PointerEvent 的结果
  6. 使用 eventnative 运行 从原生事件设置 MouseEvent 属性
  7. event.screenX 不是整数值,则将其舍入。
  8. event.screenY 不是整数值,则将其舍入。
  9. target 处对 event 执行 派发

4.2.17 处理原生鼠标移动

警告

本节需要修订。

  1. native 为原生鼠标移动
    问题 2

    该算法对 PointerEvents 的派发做出了一些假设,因为目前尚未明确规定。一旦 pointerevents/285 得到解决,这里可能需要更新。

  2. target 为使用从 native 获取的相对于视口的坐标来运行 命中测试 的结果
  3. targetDomPathtarget包含自身的祖先
  4. 为离开当前元素生成事件:
    1. 如果 上一个鼠标元素 已定义且不等于 target,则
      1. mouseout 为使用 "mouseout" 与 上一个鼠标元素 运行 创建可取消的 MouseEvent 的结果
        编辑者注

        TODO:从 native 设置 mouseout 属性。+CSSOM 属性。

    2. 使用 mouseout 运行 可能发送 pointerout 事件
    3. target 处对 mouseout 执行 派发
      注意

      需要验证被取消时的行为(看起来没有效果)。

    4. leaveElements 为从 上一个 鼠标 DOM 路径 复制得到的列表,并移除所有与 targetDomPath 共有的元素。
    5. leaveElements 中的每个 element,执行:
      编辑者注

      处理 element 已被删除的情况。 也处理其已被移动的情况:DOM 变更是否应触发 mouseleave 事件? 我们现在是否应发送?还是应丢弃? 需要验证当前浏览器的行为。

      1. mouseleave 为使用 "mouseleave" 与 element 运行 创建不可取消的 MouseEvent 的结果
      2. 设置 mouseleave.Event.composed = false
        注意

        检查兼容性:event.composed 的值。规范中为 false。Chrome/Linux = true。Firefox/Linux = false。

      3. 使用 mouseleave 运行 可能发送 pointerleave 事件
      4. result 为在 element 处对 mouseleave 执行 派发 的结果
  5. 为进入新元素生成事件:
    1. 如果 target 不是 上一个鼠标元素,则
      1. mouseover 为使用 "mouseover" 与 target 运行 创建可取消的 MouseEvent 的结果
        编辑者注

        TODO:从 native 设置 mouseout 属性。+CSSOM 属性。

      2. 使用 mouseover 运行 可能发送 pointerover 事件
      3. target 处对 mouseout 执行 派发
        注意

        需要验证被取消时的行为(看起来没有效果)。

      4. enterElements 为从 targetDomPath 复制得到的列表,并移除所有与 上一个鼠标 DOM 路径 共有的元素。
      5. enterElements 中的每个 element,执行:
        注意

        处理 element 已被删除或移动的情况。

        1. mouseenter 为使用 "mouseenter" 与 element 运行 创建不可取消的 MouseEvent 的结果
        2. 设置 mouseenter.Event.composed = false
          注意

          检查兼容性:event.composed 的值。规范中为 false。 Chrome/Linux = true。 Firefox/Linux = false。

        3. 使用 mouseenter 运行 可能发送 pointerenter 事件
          注意

          检查 shadow DOM 元素的兼容性。Chrome/Linux 会在该元素与 shadow root 上触发此事件。

        4. result 为在 element 处对 mouseenter 执行 派发 的结果
      6. 上一个鼠标元素 设为 target
      7. 上一个鼠标 DOM 路径 设为 targetDomPath
  6. mousemove 为使用 "mousemove" 与 element 运行 创建可取消的 MouseEvent 的结果
  7. 设置 mousemove 的 PointerLock 属性
  8. 使用 mousemove 运行 可能发送 pointermove 事件
  9. element 处对 mousemove 执行 派发

4.2.18 可能显示上下文菜单

警告

本节需要修订。

  1. native 为原生 mousedown 或 pointer 事件
  2. target 为该事件的 EventTarget
    1. menuevent 为使用 "contextmenu" 与 target 运行 创建一个 PointerEvent 的结果
    2. native 有效,则
      1. 使用 native 运行 从原生事件设置 MouseEvent 属性
    3. result 为在 target 处对 menuevent 执行 派发 的结果
    4. result 为 true,则显示用户代理上下文菜单
注意

为处理由键盘触发的上下文菜单,请以 native = null 且 target = 当前聚焦元素 调用此算法。

4.3 鼠标事件顺序

本规范中定义的某些鼠标事件相互之间 MUST 按固定顺序发生。下面展示了当指向设备的光标移动到某个元素上方时, MUST 发生的事件序列:

# 事件类型 元素 备注
1 mousemove
指向设备移动进入元素 A……
2 mouseover A
3 mouseenter A
4 mousemove A 多个 mousemove 事件
指向设备移出元素 A……
5 mouseout A
6 mouseleave A

当指向设备移入元素 A,然后移入嵌套元素 B,再移出时,以下事件序列 MUST 发生:

事件类型 元素 备注
1 mousemove
指向设备移动进入元素 A……
2 mouseover A
3 mouseenter A
4 mousemove A 多个 mousemove 事件
指向设备移动进入嵌套元素 B……
5 mouseout A
6 mouseover B
7 mouseenter B
8 mousemove B 多个 mousemove 事件
指向设备从元素 B 移动进入 A……
9 mouseout B
10 mouseleave B
11 mouseover A
12 mousemove A 多个 mousemove 事件
指向设备移出元素 A……
13 mouseout A
14 mouseleave A

有时可以使用 CSS 使元素在视觉上重叠。在下例中,标记为 A、B 和 C 的三个元素在网页上都具有相同的尺寸与绝对位置。元素 C 是 B 的子元素,而 B 是 A 的子元素(在 DOM 中):

Graphical representation of three stacked elements all on top of each other. The bottom element is labeled A and the top element is C
2 三个重叠元素全部叠在一起的图示表示,指向设备在该叠层上移动。

当指向设备从元素叠层之外移动到标记为 C 的元素上,然后再次移出时,以下事件序列 MUST 发生:

事件类型 元素 备注
1 mousemove
指向设备移动进入元素 C(叠层中最上方的元素)
2 mouseover C
3 mouseenter A
4 mouseenter B
5 mouseenter C
6 mousemove C 多个 mousemove 事件
指向设备移出元素 C……
7 mouseout C
8 mouseleave C
9 mouseleave B
10 mouseleave A
注意

mouseover/mouseout 事件只触发一次,而 mouseenter/mouseleave 事件会触发三次(每个元素各一次)。

以下是在某个元素上方按下并释放与指向设备相关的按钮(例如鼠标按钮或触控板)时,典型的事件序列:

事件类型 备注
1 mousedown
2 mousemove OPTIONAL,多个事件,存在某些限制
3 mouseup
4 click
5 mousemove OPTIONAL,多个事件,存在某些限制
6 mousedown
7 mousemove OPTIONAL,多个事件,存在某些限制
8 mouseup
9 click
10 dblclick
注意

mousedownmouseup 事件之间,仍允许触发 clickdblclick 事件的情况下,所允许的 mousemove 事件的滞后时间、程度、距离以及数量,将取决于具体实现、设备与平台。 这种容差可以帮助存在身体障碍(例如手部不稳)的用户在使用指向设备交互时更容易操作。

每种实现将确定合适的 滞后 容差,但总体上 SHOULD 在相关 mousedownmouseup 事件的目标为同一元素,且期间没有插入 mouseoutmouseleave 事件时触发 clickdblclick 事件;并且在相关 mousedownmouseup 事件目标不同的情况下,SHOULD 在最近的共同包含自身的祖先上触发 clickdblclick 事件。

如果一个 mousedown 事件以某 HTML 文档的 body 元素为目标,而对应的 mouseup 事件以 文档元素为目标, 那么 click 事件将被派发到 文档 元素,因为它是最近的共同包含自身的祖先。

如果在鼠标事件序列期间,目标(例如目标元素)从 DOM 中被移除,则该序列中剩余的事件 MUST NOT 在该元素上触发。

如果目标元素因 mousedown 事件而从 DOM 中移除,则不会向该元素派发 mouseupclickdblclick,也不会派发任何默认的激活事件。 但是,mouseup 事件仍会在初始目标元素被移除后、鼠标下方暴露出来的元素上被派发。 同理,如果目标元素在派发某个 mouseup 事件期间从 DOM 中被移除,则 click 以及后续事件将不会被派发。

4.4 鼠标事件类型

鼠标事件类型如下所列。在嵌套元素的情况下, 鼠标事件类型总是以最深层嵌套的元素为目标。 目标元素的祖先元素 MAY 使用冒泡来获得 其后代元素内发生的鼠标事件通知。

4.4.1 auxclick

类型 auxclick
接口 PointerEvent
同步 / 异步 同步
冒泡
受信任目标 Element
可取消
可组合
默认动作 视情况而定
上下文
(受信任事件)

当用户按下并释放非主指针按钮,或以其他方式激活指针以模拟此动作时,auxclick 事件类型 MUST 被派发到由指针所指示的 最顶层 事件目标 上。鼠标按钮的触发方式取决于指针设备与环境配置,例如,它 MAY 取决于屏幕位置或指向设备按钮按下与释放之间的延迟。

auxclick 事件应仅对非主指针按钮触发 (即当 button 值不为 0buttons 值大于 1 时)。主按钮 (例如标准鼠标的左键)MUST NOT 触发 auxclick 事件。对应主按钮的事件参见 click

auxclick 事件 MAY 之前会在同一元素上触发 mousedownmouseup 事件(忽略其他节点类型之间的变化,例如文本节点)。 取决于环境配置,当按下与释放指向设备按钮之间发生一个或多个事件类型 mouseovermousemovemouseout 时, auxclick 事件 MAY 被派发。

auxclick 事件类型的 默认动作 会根据事件的 目标 以及 buttonbuttons 属性的值而变化。auxclick 事件类型的典型默认动作如下:

  • 如果该 目标 具有关联的激活行为,则 默认 动作 MUST 为执行该激活行为。
Example 6: 接收并处理中键 auxclick
myLink.addEventListener("auxclick", function(e) {
  if (e.button === 1) {
    // This would prevent the default behavior which is for example
    // opening a new tab when middle clicking on a link.
    e.preventDefault();
    // Do something else to handle middle button click like taking
    // care of opening link or non-link buttons in new tabs in a way
    // that fits the app. Other actions like closing a tab in a tab-strip
    // which should be done on the click action can be done here too.
  }
});
注意

在右键的情况下,auxclick 事件会在 任意 contextmenu 事件之后被派发。注意,某些用户代理会在上下文菜单显示期间吞掉所有输入 事件,因此在此类场景下应用可能无法获得 auxclick。 更多说明见 Example 7

Example 7: 接收并处理右键 auxclick
myDiv.addEventListener("contextmenu", function(e) {
  // This call makes sure no context menu is shown
  // to interfere with page receiving the events.
  e.preventDefault();
});
myDiv.addEventListener("auxclick", function(e) {
  if (e.button === 2) {
    // Do something else to handle right button click like opening a
    // customized context menu inside the app.
  }
});

4.4.2 click

类型 click
接口 PointerEvent
同步 / 异步 同步
冒泡
受信任目标 Element
可取消
可组合
默认动作 视情况而定
上下文
(受信任事件)

当用户按下并释放主指针按钮,或以其他方式激活指针以模拟此动作时,click 事件类型 MUST 被派发到由指针所指示的 最顶层 事件目标 上。鼠标按钮的触发方式取决于指针设备与环境配置,例如,它 MAY 取决于屏幕位置或指向设备按钮按下与释放之间的延迟。

click 事件应仅对主指针按钮触发 (即当 button 值为 0buttons 值为 1 时)。 次要按钮 (例如标准鼠标的中键或右键)MUST NOT 触发 click 事件。对应非主按钮的事件参见 auxclick

click 事件 MAY 之前会在同一元素上触发 mousedownmouseup 事件(忽略其他节点类型之间的变化,例如文本节点)。 取决于环境配置,当按下与释放指向设备按钮之间发生一个或多个事件类型 mouseovermousemovemouseout 时, click 事件 MAY 被派发。click 事件 MAY 也可能随后触发 dblclick 事件。

如果用户在某个 <p> 元素的一个文本节点子节点上按下鼠标(该 <p> 元素被设置了很大的 line-height),然后稍微移动鼠标,使其不再位于包含文本的区域上方,但仍处于该 <p> 元素的包含块之内(即指针位于同一文本块的两行之间,但本身并未悬停在文本节点之上),随后释放鼠标,则这很可能仍会触发一个 click 事件(如果它落在 click 的正常时间性滞后范围内),因为用户仍在同一元素的范围内。注意,由用户代理生成的鼠标事件不会派发到文本节点上。

除了与指针设备关联之外, click 事件类型 MUST 也必须作为元素激活的一部分被派发。

注意

为获得最佳可访问性,建议内容作者在为自定义控件定义激活行为时使用 click 事件类型,而不是诸如 mousedownmouseup 等更具设备特性的指向设备事件类型。 虽然 click 事件类型起源于指针设备(例如鼠标),但后续的实现增强已将其扩展到超出这种关联的范围,它可以被视为用于元素激活的、与设备无关的事件类型。

click 事件类型的 默认动作 会根据事件的 目标 以及 buttonbuttons 属性的值而变化。click 事件类型的典型默认动作如下:

4.4.3 contextmenu

类型 contextmenu
接口 PointerEvent
同步 / 异步 同步
冒泡
受信任目标 Element
可取消
可组合
默认动作 如果支持,则调用上下文菜单。
上下文
(受信任事件)

用户代理 MUST 在调用上下文菜单之前派发此事件。

contextmenu 事件由鼠标右键触发时, contextmenu 事件 MUSTmousedown 事件之后被派发。

注意

取决于平台,contextmenu 事件可能在 mouseup 事件之前或之后被派发。

4.4.4 dblclick

类型 dblclick
接口 MouseEvent
同步 / 异步 同步
冒泡
受信任目标 Element
可取消
可组合
默认动作
上下文
(受信任事件)

当用户在某个元素上方用指向设备的主按钮点击两次时,用户代理 MUST 派发此事件。双击的定义取决于环境配置,但要求在 mousedownmouseupdblclick 之间,事件目标 MUST 相同。 如果点击与双击同时发生,则此事件类型 MUSTclick 事件类型之后派发;否则在 mouseup 事件类型之后派发。

click 事件一样,dblclick 事件应仅对主指针按钮触发。次要按钮 MUST NOT 触发 dblclick 事件。

注意

取消 click 事件不会影响 dblclick 事件的触发。

click 事件类型一样,默认动作dblclick 事件类型中会根据事件的 目标 以及 buttonbuttons 属性的值而变化。dblclick 事件类型的典型默认动作click 事件类型的典型默认动作相同。

4.4.5 mousedown

类型 mousedown
接口 MouseEvent
同步 / 异步 同步
冒泡
受信任目标 Element
可取消
可组合
默认动作 视情况而定:开始拖放操作;开始文本选择;开始滚动/平移交互 (如果支持,则与鼠标中键结合)
上下文
(受信任事件)
用户代理 MUST 在指向设备按钮在某个元素上方被按下时派发此事件。
注意

许多实现使用 mousedown 事件来开始多种依赖上下文的默认动作。如果取消该事件,则可以阻止这些默认动作。某些默认动作可能包括:开始对图像或链接的拖放交互、开始文本选择等。 此外,一些实现提供鼠标驱动的平移功能:当派发 mousedown 事件时,若鼠标中键处于按下状态,该功能会被激活。

4.4.6 mouseenter

类型 mouseenter
接口 MouseEvent
同步 / 异步 同步
冒泡
受信任目标 Element
可取消
可组合
默认动作
上下文
(受信任事件)
用户代理 MUST 在指向设备移动到某个元素或其后代元素的边界上时派发此事件。用户代理 MUST 也必须在该元素或其后代元素移动到主指向设备下方时派发此事件。 此事件类型与 mouseover 类似, 但不同之处在于它不冒泡,且当指针设备从某元素移动到其某个后代元素的边界上时,MUST NOT 派发该事件。
注意

此事件类型与 CSS 的 :hover 伪类 [CSS2] 之间存在相似之处。另请参见 mouseleave 事件类型。

4.4.7 mouseleave

类型 mouseleave
接口 MouseEvent
同步 / 异步 同步
冒泡
受信任目标 Element
可取消
可组合
默认动作
上下文
(受信任事件)
用户代理 MUST 在指向设备移出某元素及其所有后代元素的边界时派发此事件。用户代理 MUST 也必须在该元素或其后代元素移动到不再位于主指向设备下方时派发此事件。 此事件类型与 mouseout 类似, 但不同之处在于它不冒泡,并且在指向设备离开该元素的边界以及其所有子元素的边界之前,MUST NOT 派发该事件。
注意

此事件类型与 CSS 的 :hover 伪类 [CSS2] 之间存在相似之处。另请参见 mouseenter 事件类型。

4.4.8 mousemove

类型 mousemove
接口 MouseEvent
同步 / 异步 同步
冒泡
受信任目标 Element
可取消
可组合
默认动作
上下文
(受信任事件)
用户代理 MUST 在指向设备位于某元素上方时移动指向设备而派发此事件。指向设备移动期间的事件触发频率取决于具体实现、设备与平台,但对于持续的指针设备移动,SHOULD 触发多个连续的 mousemove 事件,而不是对每次鼠标移动仅触发单个事件。鼓励实现确定最佳的频率,以在响应性与性能之间取得平衡。
注意

在某些实现环境(如浏览器)中,如果用户开始了拖动操作(例如按下鼠标按钮)且指向设备已离开用户代理的边界,mousemove 事件仍可能继续触发。

注意

此事件曾在 DOM Level 2 Events 中被规定为不可取消,但后来进行了更改,以反映用户代理之间现有的互操作性。

4.4.9 mouseout

类型 mouseout
接口 MouseEvent
同步 / 异步 同步
冒泡
受信任目标 Element
可取消
可组合
默认动作
上下文
(受信任事件)
用户代理 MUST 在指向设备移出某元素的边界时,或该元素移动到不再位于主指向设备下方时派发此事件。 此事件类型与 mouseleave 类似,但不同之处在于它会冒泡,并且当指针设备从某元素移动到其某个后代元素的边界上时,它 MUST 被派发。
注意

另请参见 mouseover 事件类型。

4.4.10 mouseover

类型 mouseover
接口 MouseEvent
同步 / 异步 同步
冒泡
受信任目标 Element
可取消
可组合
默认动作
上下文
(受信任事件)
用户代理 MUST 在指向设备移动到某个元素的边界上时,或该元素移动到主指向设备下方时派发此事件。 此事件类型与 mouseenter 类似,但不同之处在于它会冒泡,并且当指针设备移动到某个元素的边界上,且该元素的祖先元素是同一 目标 上相同 事件监听器 实例的目标时,它 MUST 被派发。
注意

另请参见 mouseout 事件类型。

4.4.11 mouseup

类型 mouseup
接口 MouseEvent
同步 / 异步 同步
冒泡
受信任目标 Element
可取消
可组合
默认动作
上下文
(受信任事件)
用户代理 MUST 在指向设备按钮在某个元素上方被释放时派发此事件。
注意

在某些实现环境(如浏览器)中,即使指向设备已离开用户代理的边界,仍可能派发 mouseup 事件,例如用户在按下鼠标按钮的情况下开始了拖动操作。

5. 指针事件与接口

5.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;
    long        persistentDeviceId = 0;
    sequence<PointerEvent> coalescedEvents = [];
    sequence<PointerEvent> predictedEvents = [];
};

[Exposed=Window]
interface PointerEvent : MouseEvent {
    constructor(DOMString type, optional PointerEventInit eventInitDict = {});
    readonly        attribute long        pointerId;
    readonly        attribute double      width;
    readonly        attribute double      height;
    readonly        attribute float       pressure;
    readonly        attribute float       tangentialPressure;
    readonly        attribute long        tiltX;
    readonly        attribute long        tiltY;
    readonly        attribute long        twist;
    readonly        attribute double      altitudeAngle;
    readonly        attribute double      azimuthAngle;
    readonly        attribute DOMString   pointerType;
    readonly        attribute boolean     isPrimary;
    readonly        attribute long        persistentDeviceId;
    [SecureContext] sequence<PointerEvent> getCoalescedEvents();
    sequence<PointerEvent> getPredictedEvents();
};
pointerId

导致该事件的指针的唯一标识符。用户代理 MAY 为主鼠标指针保留一个通用的 pointerId01pointerId 的值 -1 MUST 被保留并用于指示由指向设备以外的其他来源生成的事件。对于任何其他指针,用户 代理可自由采用不同策略与方法来分配 pointerId 值。然而,在顶层 浏览上下文(如 [HTML] 所定义)中,所有 活动指针都必须是唯一的,并且该标识符 MUST NOT 受到任何其他顶层浏览 上下文的影响(即:某个顶层浏览上下文不能假定,当指针移出该浏览上下文并进入另一个顶层浏览上下文时,该指针的 pointerId 仍会保持不变)。

用户代理 MAY 回收先前已退役的、来自之前活动指针的 pointerId 值,或者它 MAY 始终为某个特定指向设备重复使用同一个 pointerId (例如:在多用户协作应用中,用于唯一识别来自特定用户的某支笔/触控笔输入)。然而,在后一种情况下,为了尽量降低跨不同页面或域的指纹识别与跟踪风险,pointerId MUST 仅在页面/会话的生命周期内与该特定指向设备显式关联,并且下次在新的会话中再次使用该特定指向设备时,MUST 选择一个新的随机 pointerId

注意

pointerId 的选择算法取决于实现。作者不能假设这些值传达除“与所有其他活动指针都不同的指针标识符”之外的任何特定含义。例如,用户代理可能只是按活动指针变为活动的顺序,从 0 开始为其分配一个数字——但这些值不保证是单调递增的。由于某个特定指向设备是否复用同一个 pointerId 取决于各实现,强烈不建议作者依赖此行为,而应改为使用 persistentDeviceId

width

指针的接触几何的宽度(X 轴方向的量级),以 CSS 像素表示(见 [CSS21])。对于给定指针,该值 MAY 在每个事件中被更新。对于通常不具备接触几何的输入(如传统鼠标),以及硬件无法检测到实际输入几何的情况,用户代理 MUST 返回默认值 1

height

指针的接触几何的高度(Y 轴方向的量级),以 CSS 像素表示(见 [CSS21])。对于给定指针,该值 MAY 在每个事件中被更新。对于通常不具备接触几何的输入(如传统鼠标),以及硬件无法检测到实际输入几何的情况,用户代理 MUST 返回默认值 1

pressure

指针输入的归一化压力值,范围为 [0,1],其中 01 分别表示硬件能够检测到的最小与最大压力。对于不支持压力的硬件与平台,当处于活动按钮状态时,该值 MUST0.5,否则为 0

tangentialPressure

归一化切向压力(也称为笔杆压力),通常由额外控制器设置(例如喷枪式触控笔上的指轮),其取值范围为 [-1,1],其中 0 为控制器的中立位置。注意,某些硬件可能仅支持 [0,1] 范围内的正值。对于不支持切向压力的硬件与平台,该值 MUST0

注意
尽管该属性名如此,但在实践中,为该属性生成值的硬件控制器/传感器不一定对压力敏感。例如,在大多数情况下,大多数喷枪/绘画触控笔实现中的指轮可以自由设置,而不是要求用户对指轮持续施加压力以防止其回到零位置。
tiltX

Y-Z 平面与同时包含换能器(例如笔/触控笔)轴线与 Y 轴的平面之间的平面角(以度为单位,范围 [-90,90])。正的 tiltX 表示向右,即沿 X 值增大的方向。 tiltX 可与 tiltY 一起使用,以表示带数字化仪的换能器偏离其法线的倾斜。对于不报告倾斜或角度的硬件与平台,该值 MUST0

tiltX 说明图
3 正的 tiltX
tiltY

X-Z 平面与同时包含换能器(例如笔/触控笔)轴线与 X 轴的平面之间的平面角(以度为单位,范围 [-90,90])。正的 tiltY 表示朝向用户,即沿 Y 值增大的方向。 tiltY 可与 tiltX 一起使用,以表示带数字化仪的换能器偏离其法线的倾斜。对于不报告倾斜或角度的硬件与平台,该值 MUST0

tiltY 说明图
4 正的 tiltY
twist

换能器(例如笔/触控笔)绕其自身主轴顺时针旋转的角度(以度为单位,范围 [0,359])。对于不报告 twist 的硬件与平台,该值 MUST0

altitudeAngle

换能器(例如笔/触控笔)的高度角(以弧度为单位),范围为 [0,π/2]——其中 0 表示与表面(X-Y 平面)平行,π/2 表示与表面垂直。对于不报告倾斜或角度的硬件与平台,该值 MUSTπ/2

注意
此处为 altitudeAngle 定义的默认值是 π/2, 这会使换能器处于垂直于表面的姿态。 这与 Touch Events - Level 2 规范对 altitudeAngle 属性的定义不同,后者的默认值为 0
altitudeAngle 说明图
5 示例: altitudeAngleπ/4(与 X-Y 平面成 45 度)。
azimuthAngle

换能器(例如笔/触控笔)的方位角(以弧度为单位),范围为 [0, 2π]——其中 0 表示换能器的笔帽指向 X 值增大的方向(在 X-Y 平面上俯视时指向“3 点钟”方向),并且数值在顺时针方向逐步增大(π/2 为“6 点钟”、π 为“9 点钟”、3π/2 为“12 点钟”)。当换能器完全垂直于表面(altitudeAngleπ/2)时,该值 MUST0。对于不报告倾斜或角度的硬件与平台,该值 MUST0

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

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

指针设备类型 pointerType
Mouse mouse
Pen / stylus pen
Touch contact touch

如果用户代理无法检测设备类型,则该值 MUST 为空字符串。如果用户代理支持上述列表之外的指针设备类型,为避免不同设备类型名称冲突,pointerType 的值 SHOULD 使用厂商前缀。未来规范 MAY 为其他设备类型提供额外的规范化取值。

注意
参见 Example 2,其中对 pointerType 的基本用法作了演示。另请注意,开发者应包含某种形式的默认处理,以涵盖用户代理可能实现了自定义 pointerType 值的情况,以及 pointerType 仅为空字符串的情况。
isPrimary

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

persistentDeviceId

指向设备的唯一标识符。如果硬件支持多个指针,则由指向设备生成的指针事件 MUST 仅在这些指针在会话期间可被唯一识别时,才会获得 persistentDeviceId。如果指针可被唯一识别,则分配给该指向设备的 persistentDeviceId 在该会话剩余时间内保持不变。persistentDeviceId 的值 0 MUST 被保留并用于指示其生成设备无法被识别的事件。与 pointerId 类似,为了尽量降低跨不同页面或域的指纹识别与跟踪风险,persistentDeviceId MUST 仅在页面/会话的生命周期内与该特定指向设备显式关联,并且下次在新的会话中再次使用该特定指向设备时,MUST 选择一个新的随机 persistentDeviceId

注意
由于数字化仪与指向设备硬件约束,不能保证指向设备的所有指针事件都可获得 persistentDeviceId。例如,设备可能无法及时向数字化仪报告其硬件 id,使得 pointerdown 时还没有 persistentDeviceId。在这种情况下,persistentDeviceId 可能最初为 0,随后才变为有效值。
getCoalescedEvents()

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

getPredictedEvents()

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

PointerEventInit 字典由 PointerEvent 接口的构造函数使用,用于提供一种机制来构造不受信任(合成)的指针事件。它继承自 [UIEVENTS] 中定义的 MouseEventInit 字典。参见 示例,其中提供了演示如何触发不受信任指针事件的示例代码。

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

注意
PointerEvent 接口继承自 MouseEvent(定义于 UI Events)。 另请注意 CSSOM View Module 中提出的扩展,它将各种坐标属性从 long 改为 double,以允许小数坐标。对于已经为 PointerEvent 实现了该提议扩展、但 没有 为普通 MouseEvent 实现该扩展的用户代理,在处理 click, auxclickcontextmenu 事件时会有额外要求。

5.1.1 按钮状态

5.1.1.1 组合按键交互

一些指针设备(如鼠标或笔)支持多个按钮。在 [UIEVENTS] 的鼠标事件模型中,每次按下按钮都会产生一个 mousedown 与一个 mouseup 事件。为了更好地抽象这一硬件差异并简化跨设备输入的编写,指针事件在组合按键按下(当指针设备上的一个按钮已处于按下状态时,再按下另一个按钮)时,不会触发重叠的 pointerdownpointerup 事件。

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

buttonbuttons 属性的修改仅适用于指针事件。然而,对于 clickauxclickcontextmenubuttonbuttons 的取值 MUST 遵循 [UIEVENTS](与兼容鼠标事件的情况相同)。

5.1.1.2 button 属性

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

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

buttons 属性以位掩码形式给出设备按钮的当前状态(与 MouseEvent 相同,但可能取值集合更大)。

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

5.1.2 主指针

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

  • 在任何给定时刻,每种指针类型最多只能有一个主指针。
  • 对某一特定指针类型,最先变为活动状态的指针(例如在多点触控交互中第一个触碰屏幕的手指)会成为该指针类型的主指针。
  • 只有主指针才会产生兼容鼠标事件。当存在多个主指针时,这些指针都会产生兼容鼠标事件
注意
希望实现单指针交互的作者可以通过忽略非主指针来达成(但请参见下方关于多个主指针的注意事项)。
注意
当同时使用两种或以上的指针设备类型时,会存在多个主指针(每个 pointerType 一个)。例如,同时发生触摸接触与鼠标光标移动时,会产生两个都被视为主指针的指针。
注意
某些设备、操作系统与用户代理可能会忽略同时使用多种指针输入,以避免误操作。例如,既支持触摸又支持笔交互的设备在笔被主动使用时可能会忽略触摸输入,从而允许用户在使用笔时将手放在触摸屏上(这一特性通常称为“掌心防误触/palm rejection”)。目前作者无法抑制此行为。
注意
在某些情况下,用户代理可能会触发指针事件,但其中没有任何指针被标记为主指针。例如,当某一类型存在多个活动指针(如多点触控交互)且主指针被移除(例如离开屏幕)时,可能最终不存在主指针。又或者在某些平台中,主指针是使用设备上同类型的所有活动指针来确定的(包括那些目标指向非用户代理应用的指针);如果第一个(主)指针在用户代理之外,而其他(非主)指针的目标在用户代理之内,则用户代理 MAY 为其他指针触发指针事件,并使 isPrimaryfalse
注意
当前操作系统与用户代理通常没有“多个鼠标输入”的概念。当存在多个鼠标设备时(例如笔记本既有触控板又接了外置鼠标),所有鼠标设备通常会被当作单一设备处理——来自任一设备的移动都会被转换为单个鼠标指针的移动,并且不会区分不同鼠标设备的按键按下。因此通常只会有一个鼠标指针,且该指针为主指针。

5.1.3 使用 PointerEvent 接口触发事件

“以名称 e 触发一个指针事件”是指:使用 PointerEvent触发名为 e 的事件,其属性按 PointerEvent 接口以及 属性与默认动作中的定义进行设置。

如果该事件不是 gotpointercapturelostpointercaptureclickauxclickcontextmenu 事件,则对该 PointerEvent 运行处理待定指针捕获步骤。

确定目标(即事件将触发于其上的目标)如下:

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

如果事件为 pointerdownpointermovepointerup,则将该事件的 活动文档 (对应该事件的 pointerId)设置为 targetDocument

如果事件为 pointerdown,关联设备为直接操控设备,并且目标为 Element, 则按隐式指针捕获中的描述,为该 pointerId设置指针捕获到目标元素。

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

将事件触发到已确定的目标上。

将已确定的目标保存为该指针的 previousTarget, 并将 needsOverEvent 标志重置为 false。 如果某个时刻 previousTarget 将不再连接 [DOM], 则将 previousTarget 更新为沿与向 previousTarget 派发事件相对应的事件路径中,最近的仍连接 [DOM] 的父节点, 并将 needsOverEvent 标志设为 true

注意
指针捕获目标覆盖作为目标、而不是普通命中测试结果,可能会触发某些边界事件(由 [UIEVENTS] 定义)。这与指针离开其先前目标并进入新的捕获目标相同。当捕获被释放时,也可能发生相同情况,即指针离开捕获目标并进入命中测试目标。
5.1.3.1 属性与默认动作

本规范中定义的事件类型的 bubblescancelable 属性,以及其默认动作,列于下表。各事件类型的细节见 指针事件类型

事件类型 冒泡 可取消 默认动作
pointerover
pointerenter
pointerdown 视情况而定:当指针为主指针时,为 mousedown 事件的所有默认动作
取消该事件也会阻止后续触发兼容鼠标事件
pointermove 视情况而定:当指针为主指针时,为 mousemove 的所有默认动作
pointerrawupdate
pointerup 视情况而定:当指针为主指针时,为 mouseup 的所有默认动作
pointercancel
pointerout
pointerleave
gotpointercapture
lostpointercapture

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

对于 pointerenterpointerleave 事件,其 composed [DOM] 属性 SHOULDfalse;对上表中所有其他指针事件,该属性 SHOULDtrue

对上表中所有指针事件,其 detail [UIEVENTS] 属性 SHOULD 为 0。

注意
许多用户代理在 MouseEvent 中暴露了非标准属性 fromElementtoElement 以支持遗留内容。我们鼓励这些用户代理将 PointerEvent 中这些(继承的)属性值设置为 null,以促使作者迁移到使用标准替代项(targetrelatedTarget)。

MouseEventrelatedTarget 类似,relatedTarget 应初始化为指针刚刚离开的元素边界对应的元素(对于 pointeroverpointerenter 事件),或指针正在进入其边界的元素(对于 pointeroutpointerleave)。对其他指针事件,该值默认为 null。注意,当某元素获得指针捕获时,该指针的后续所有事件都被认为发生在捕获元素边界之内。

对于 gotpointercapturelostpointercapture 事件,除上表中定义的那些属性之外,其余所有属性都应与触发用户代理运行处理待定指针捕获步骤并触发 gotpointercapturelostpointercapture 事件的那个 Pointer Event 相同。

5.1.3.2 处理待定指针捕获

用户代理 MUST隐式释放指针捕获以及触发非 gotpointercapturelostpointercapture 的指针事件时,运行以下步骤。

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

clickauxclickcontextmenu 事件一节所定义,即使 lostpointercapture 事件已经派发,若存在相应的 clickauxclickcontextmenu 事件,它仍会派发到捕获目标。

5.1.3.3 抑制指针事件流

用户代理 MUST 在检测到网页不太可能继续接收具有特定 pointerId 的指针事件时,抑制指针事件流。以下任一场景满足该条件(也 MAY 存在其他场景):

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

用户代理 MAY 抑制指针事件流的其他场景包括:

  • 当指针处于活动状态时,设备屏幕方向发生变化。
  • 用户尝试使用超过设备所支持数量的同时指针输入进行交互。
  • 用户代理将输入解释为意外输入(例如硬件支持掌心防误触)。

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

用户代理 MUST 运行以下步骤来抑制指针事件流

5.1.4 由布局变化引起的边界事件

相对于屏幕表面移动的指向设备,或其任一属性发生变化的指向设备,会按 指针事件类型中定义触发各类事件。对于静止的指向设备(既未相对屏幕表面移动,也未发生任何属性变化),若布局变化影响了该指针的 命中测试目标,则 用户代理 MUST 在布局变化后触发某些边界事件;详见 pointeroverpointerenterpointeroutpointerleave。出于性能原因(例如避免过多命中测试或边界事件监听器引发的布局变化),用户代理 MAY 延迟触发这些边界事件。

注意
静止的指向设备(既未相对屏幕表面移动,也未发生任何属性变化)永远不会触发 pointermove 事件。

5.1.5 tiltX / tiltYaltitudeAngle / azimuthAngle 之间的转换

Pointer Events 提供了两组互补属性,用于表达换能器相对于 X-Y 平面的朝向:tiltX / tiltY(在原始 Pointer Events 规范中引入)以及 azimuthAngle / altitudeAngle(采自 Touch Events - Level 2 规范)。

取决于具体硬件与平台,用户代理很可能只会获得换能器相对于屏幕平面的其中一组朝向值——要么是 tiltX / tiltY,要么是 altitudeAngle / azimuthAngle。用户代理 MUST 使用以下算法来转换这些值。

当用户代理从 azimuthAngle / altitudeAngle 计算出 tiltX / tiltY 时,它 SHOULD 使用 Math.round [ECMASCRIPT] 规则对最终整数值进行舍入。

Example 8: 在 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};
}

5.2 PointerEvent 算法

5.2.1 初始化一个 PointerEvent

要使用 eventeventTypeeventTargetbubblescancelable初始化一个 PointerEvent,请运行以下步骤:

  1. 使用 eventeventTypeeventTargetbubblescancelable初始化一个 MouseEvent
  2. 将所有其他属性初始化为默认的 PointerEvent 值。

5.2.2 创建一个 PointerEvent

要使用 eventTypeeventTargetbubblescancelable创建一个 PointerEvent,请运行以下步骤:

  1. event 为使用 PointerEvent 创建一个事件的结果
  2. 使用 eventeventTypeeventTargetbubblescancelable初始化一个 PointerEvent
  3. 返回 event

5.2.3 MouseEvent 创建 PointerEvent

  1. eventType 为包含该事件类型的 DOMString
  2. mouseevent 为相应的 MouseEvent
  3. event 为使用 PointerEvent 创建一个事件的结果
  4. targetmouseevent.target
  5. 使用 eventeventTypetarget初始化一个 PointerEvent
  6. MouseEvent 的属性从 mouseevent 复制到 event
  7. 返回 event

5.2.4 可能发送 pointerout 事件

  1. mouseout 为相应的 mouseout MouseEvent
  2. pointerout 为使用 "pointerout" 与 mouseout 从 MouseEvent 创建 PointerEvent 的结果
  3. 设置 pointerevent 属性
    编者注

    TODO.

  4. targetmouseout.target
  5. target派发 pointerout

5.2.5 可能发送 pointerleave 事件

  1. mouseout 为相应的 mouseout MouseEvent
  2. pointerout 为使用 "pointerout" 与 mouseout 从 MouseEvent 创建 PointerEvent 的结果
  3. 设置 pointerevent 属性
    编者注

    TODO.

  4. targetmouseout.target
  5. target派发 pointerout

5.2.6 可能发送 pointerover 事件

  1. mouseout 为相应的 mouseout MouseEvent
  2. pointerout 为使用 "pointerout" 与 mouseout 从 MouseEvent 创建 PointerEvent 的结果
  3. 设置 pointerevent 属性
    编者注

    TODO.

  4. targetmouseout.target
  5. target派发 pointerout

5.2.7 可能发送 pointerenter 事件

  1. mouseout 为相应的 mouseout MouseEvent
  2. pointerout 为使用 "pointerout" 与 mouseout 从 MouseEvent 创建 PointerEvent 的结果
  3. 设置 pointerevent 属性
    编者注

    TODO.

  4. targetmouseout.target
  5. target派发 pointerout

5.2.8 可能发送 pointermove 事件

  1. mouseout 为相应的 mouseout MouseEvent
    编者注

    这是否可以发送 pointermove 与 pointerrawupdate?还是我们需要 2 个方法?

    编者注

    为了正确定义 pointermove 事件如何被合并,需要什么?

  2. pointerout 为使用 "pointerout" 与 mouseout 从 MouseEvent 创建 PointerEvent 的结果
  3. 设置 pointerevent 属性
    编者注

    TODO.

  4. targetmouseout.target
  5. target派发 pointerout

5.2.9 可能发送 pointerdown 事件

  1. mouseout 为相应的 mouseout MouseEvent
    注意

    mousedown 事件不同,按下多个按钮时 pointerdown 事件不会嵌套触发。 传入 MouseEvent 是为了将其字段复制到 PointerEvent 中。

  2. pointerout 为使用 "pointerout" 与 mouseout 从 MouseEvent 创建 PointerEvent 的结果
  3. 设置 pointerevent 属性
    编者注

    TODO.

  4. targetmouseout.target
  5. target派发 pointerout

5.2.10 可能发送 pointerrawupdate 事件

  1. mouseout 为相应的 mouseout MouseEvent
  2. targetmouseout.target
  3. target派发 pointerout

5.2.11 可能发送 pointerup 事件

  1. mouseout 为相应的 mouseout MouseEvent
    注意

    mouseup 事件不同,按下多个按钮时 pointerup 事件不会嵌套触发。 传入 MouseEvent 是为了将其字段复制到 PointerEvent 中。

  2. pointerout 为使用 "pointerout" 与 mouseout 从 MouseEvent 创建 PointerEvent 的结果
  3. 设置 pointerevent 属性
    编者注

    TODO.

  4. targetmouseout.target
  5. target派发 pointerout

5.3 Pointer 事件类型

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

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

5.3.1 pointerover 事件

用户代理 MUST 在发生以下任一情况时,触发一个指针事件,其名称为 pointerover

5.3.2 pointerenter 事件

用户代理 MUST 在发生以下任一情况时,触发一个指针事件,其名称为 pointerenter

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

5.3.3 pointerdown 事件

用户代理 MUST 当指针进入活动按钮状态时,触发一个指针事件,其名称为 pointerdown。对鼠标而言,即设备从没有任何按钮按下转换为至少有一个按钮按下时。对触摸而言,即与数字化仪发生物理接触时。对笔而言,即笔在没有任何按钮按下的情况下与数字化仪发生物理接触,或在悬停时从没有任何按钮按下转换为至少有一个按钮按下时。

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

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

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

5.3.4 pointermove 事件

用户代理 MUST 当指针发生任何不会触发 pointerdownpointerup 事件的属性变化时,触发一个指针事件,其名称为 pointermove。这包括坐标、压力、切向压力、倾斜、旋转、接触几何(widthheight)或 组合按键 的任何变化。

用户代理 MAY 延迟派发 pointermove 事件(例如出于性能原因)。 合并事件信息将通过getCoalescedEvents 方法暴露给单个已派发的 pointermove 事件。 这类事件的最终坐标应当用于查找该事件的目标。

5.3.5 pointerrawupdate 事件

用户代理 MUST 触发一个指针事件,其名称为 pointerrawupdate,并且仅能在安全上下文中这样做;当指针发生任何不会触发 pointerdownpointerup 事件的属性变化时触发。此类属性列表参见 pointermove 事件。

pointermove 不同,用户代理 SHOULD 尽快并尽可能频繁地派发 pointerrawupdate 事件,频率以 JavaScript 能处理这些事件为限。

pointerrawupdate 事件的 target 可能与 pointermove 事件不同, 因为 pointermove 事件可能被延迟或合并,并且用于查找 target 的事件最终位置可能与其合并事件不同。

请注意:如果事件循环中已经存在另一个具有相同 pointerId 且尚未在事件循环中被派发的 pointerrawupdate,那么 用户代理 MAY 将新的 pointerrawupdate 与该事件合并,而不是创建一个新的 任务。 这可能导致 pointerrawupdate 拥有合并事件,并且它们将作为一个 pointerrawupdate 事件的合并事件,在该事件于事件循环中被处理时尽快交付。 更多信息参见getCoalescedEvents

pointerrawupdatepointermove 的顺序而言: 如果用户代理从平台接收到一次更新,该更新会同时导致 pointerrawupdatepointermove 事件, 则 用户代理 MUST 在派发相应的 pointermove 之前,先派发 pointerrawupdate 事件。

target 之外,自上次 pointermove 事件以来,所有已派发的 pointerrawupdate 事件的合并事件列表的串联,在其他事件属性方面,与下一个 pointermove 事件的合并事件相同。 pointerrawupdate 的属性大多与 pointermove 相同,例外是 cancelable:对 pointerrawupdate,它 MUST 为 false。

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

注意
pointerrawupdate 事件添加监听器,可能会根据用户代理的实现,对网页性能产生负面影响。 对大多数使用场景而言,其他 pointerevent 类型已经足够。 只有当 JavaScript 需要高频事件并且能够同样快速地处理它们时,才应添加 pointerrawupdate 监听器。 在这些情况下,可能也没有必要再监听其他类型的指针事件。

5.3.6 pointerup 事件

用户代理 MUST 当指针离开活动按钮状态时,触发一个指针事件,其名称为 pointerup。对鼠标而言,即设备从至少有一个按钮按下转换为没有任何按钮按下时。对触摸而言,即从数字化仪移除物理接触时。对笔而言,即在没有任何按钮按下时,笔与数字化仪的物理接触被移除,或在悬停时从至少有一个按钮按下转换为没有任何按钮按下时。

对于不支持悬停的输入设备,用户 代理 MUST 还必须在派发 pointerup 事件之后,触发名为 pointerout 的指针事件,再触发名为 pointerleave 的指针事件。

所有 pointerup 事件的 pressure 值都为 0

用户代理 MUST 若该指针当前被捕获,还必须隐式释放指针捕获

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

5.3.7 pointercancel 事件

用户代理 MUST 当其检测到需要抑制指针事件流的场景时,触发一个指针事件,其名称为 pointercancel

pointercancel 事件的下列属性值 MUST 与最后一次派发且具有相同 pointerId 的指针事件的值相匹配:widthheightpressuretangentialPressuretiltXtiltYtwistaltitudeAngleazimuthAnglepointerTypeisPrimary,以及从 [UIEVENTS] 继承的坐标属性。pointercancel 事件中的 coalescedEventspredictedEvents 列表 MUST 为空,并且该事件的 cancelable 属性 MUST 为 false。

5.3.8 pointerout 事件

用户代理 MUST 在发生以下任一情况时,触发一个指针事件,其名称为 pointerout

5.3.9 pointerleave 事件

用户代理 MUST 在发生以下任一情况时,触发一个指针事件,其名称为 pointerleave

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

5.3.10 gotpointercapture 事件

用户代理 MUST 当某个元素接收到指针捕获时,触发一个指针事件,其名称为 gotpointercapture。该事件会在接收指针捕获的元素上触发。该指针的后续事件将会在此元素上触发。参见设置指针捕获处理待定指针捕获章节。

5.3.11 lostpointercapture 事件

用户代理 MUST 在某个指针的指针捕获被释放之后,触发一个指针事件,其名称为 lostpointercapture。该事件 MUST 在捕获被释放后、该指针的任何后续事件之前触发。该事件会在指针捕获被移除的元素上触发。除 click, auxclickcontextmenu 事件之外,该指针的所有后续事件在确定事件目标时遵循正常的命中测试机制(不在本规范范围内)。参见释放指针捕获指针捕获的隐式释放处理待定指针捕获章节。

5.3.12 clickauxclickcontextmenu 事件

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

这些事件 MUSTPointerEvent 类型,并且必须满足本节其余部分提到的附加要求。

5.3.12.1 事件属性

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

  • 如果这些事件由指向设备生成,则它们的 pointerIdpointerType MUST 与导致这些事件的 PointerEvent 相同。
  • 如果这些事件由非指向设备生成(例如语音识别软件或键盘交互),则 pointerId MUST-1pointerType MUST 为空字符串。
5.3.12.2 事件坐标

PointerEvent 中所述,CSSOM View Module 提议将各种坐标属性 (screenXscreenYpageXpageYclientXclientYxyoffsetXoffsetY)重新定义为 double,以允许小数坐标。 但是,该变更——当只应用于 PointerEvent 而不应用于普通 MouseEvent 时——在 clickauxclickcontextmenu 的情况下已被证明会导致与遗留代码的 Web 兼容性问题。出于这个原因,那些仅在 CSSOM View Module 中对 PointerEvent 实现了该提议变更的用户代理 MUST 使用 Math.floor [ECMASCRIPT] 将 clickauxclickcontextmenu 的各种坐标属性转换为 long 值(如原始 UI Events 中定义)。

5.3.12.3 事件派发

clickauxclickcontextmenu 事件 MUST 遵循 [UIEVENTS] 规范中定义的派发流程,但事件目标要按下方算法进行覆盖:

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

    注意

    userEvent 可以是非 PointerEvent;例如,当通过在复选框元素上按下空格键导致派发 click 事件时,它是一个 KeyboardEvent

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

  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 的捕获目标。

6. 滚轮事件

滚轮(Wheel)是可以在一个或多个空间维度上旋转的设备,并且可以与某个指针设备相关联。坐标系取决于 环境配置。

用户的环境可能被配置为将垂直滚动 与沿 y 轴的旋转关联,将水平滚动与沿 x 轴的旋转关联,并将缩放与沿 z 轴的旋转关联。

WheelEvent 对象的 deltaX、deltaY 与 deltaZ 属性表示 沿各自轴向的测量值,其单位可以是像素、行或 页。所报告的测量值是在一个与环境相关的 算法将滚轮设备的实际旋转/移动转换为 适当的值与单位之后提供的。
注意

用户的环境设置可以被自定义,以不同方式解释滚轮设备的实际旋转/移动。 普通带齿槽的鼠标滚轮的一次运动可能产生 162 像素的测量值 (162 仅为示例值,实际值可能取决于用户代理当前的屏幕 尺寸)。 但用户可以更改其默认环境设置以加快鼠标滚轮速度, 从而增大这个数值。 此外,一些鼠标滚轮软件可能支持加速度(滚轮旋转/移动越快, 每次测量的 delta 越大)甚至支持亚像素的 旋转 测量。 因为这些原因,作者不能假设在某个用户代理中的给定 旋转 量会在所有用户代理中 产生相同的 delta 值。

deltaX、deltaY 与 deltaZ 属性的值的符号(正或负) MUST 在实际滚轮设备的运动在同一方向旋转/移动期间 多次派发 wheel 事件时保持一致。 如果用户代理将滚动作为 wheel 事件的默认动作, 那么 delta 的符号 SHOULD 由右手坐标系给出,其中 X、Y 与 Z 轴的正方向分别指向文档的最右边缘、最下边缘以及最远的深度(远离用户)。
注意

各个用户代理(取决于其运行环境与硬件配置) 可能会对同一物理用户交互的滚轮操作做出不同的解释。 例如,在触控板边缘从上到下进行一次垂直滑动, 可能会被解释为一种滚轮动作,其意图要么是将页面向下滚动, 要么是将页面向上平移(即分别导致 deltaY 值为正或为负)。

当第一次触发滚轮事件时,用户代理 MUST 创建一个 滚轮事件事务,以便在实现特定的一段时间内发生的所有后续滚轮事件 都能以同一个元素为目标。滚轮事件事务是一系列与单个用户手势关联的滚轮事件。 该 滚轮事件事务 MUST 具有一个关联的事件目标, 即该组中第一次滚轮事件发生时的 最上层事件目标

如果一系列以可滚动元素为目标的滚轮事件在某个子元素上方开始, 那么同一用户手势的后续事件可能会发生在该子元素之上。

6.1 接口 WheelEvent

WheelEvent 接口提供与 wheel 事件相关联的特定上下文信息。 要创建 WheelEvent 接口的一个实例,请使用 WheelEvent 构造函数, 并传入可选的 WheelEventInit 字典。

6.1.1 WheelEvent

WebIDL[Exposed=Window]
interface WheelEvent : MouseEvent {
	constructor(DOMString type, optional WheelEventInit eventInitDict = {});
	// DeltaModeCode
	const unsigned long DOM_DELTA_PIXEL = 0x00;
	const unsigned long DOM_DELTA_LINE	= 0x01;
	const unsigned long DOM_DELTA_PAGE	= 0x02;

	readonly attribute double deltaX;
	readonly attribute double deltaY;
	readonly attribute double deltaZ;
	readonly attribute unsigned long deltaMode;
};
DOM_DELTA_PIXEL
delta 的测量单位 MUST 为像素。 这是在大多数操作系统与 实现配置中的最典型情况。
DOM_DELTA_LINE
delta 的测量单位 MUST 为单独的 文本行。这适用于许多表单控件。
DOM_DELTA_PAGE
delta 的测量单位 MUST 为页面, 可以定义为单个屏幕或划分出的页。
deltaX
在默认动作是滚动的用户代理中,wheel 事件的该值 MUST 为沿 x 轴(以像素、行或页为单位)应当滚动的测量值(在事件未被取消的情况下)。 否则,该值是实现特定的测量(以像素、行或页为单位),用于表示滚轮设备绕 x 轴的移动。 该属性的未初始化值 MUST0.0
deltaY
在默认动作是滚动的用户代理中,wheel 事件的该值 MUST 为沿 y 轴(以像素、行或页为单位)应当滚动的测量值(在事件未被取消的情况下)。 否则,该值是实现特定的测量(以像素、行或页为单位),用于表示滚轮设备绕 y 轴的移动。 该属性的未初始化值 MUST0.0
deltaZ
在默认动作是滚动的用户代理中,wheel 事件的该值 MUST 为沿 z 轴(以像素、行或页为单位)应当滚动的测量值(在事件未被取消的情况下)。 否则,该值是实现特定的测量(以像素、行或页为单位),用于表示滚轮设备绕 z 轴的移动。 该属性的未初始化值 MUST0.0
deltaMode
deltaMode 属性包含对 delta 值的测量单位的指示。其 默认值为 DOM_DELTA_PIXEL(像素)。 该属性 MUST 被设置为某个 DOM_DELTA 常量之一,以指示 delta 值的测量单位。 精确的测量与设备、操作系统 以及应用配置有关。 该属性的未初始化值 MUST0

6.1.2 WheelEventInit

WebIDLdictionary WheelEventInit : MouseEventInit {
	double deltaX = 0.0;
	double deltaY = 0.0;
	double deltaZ = 0.0;
	unsigned long deltaMode = 0;
};
deltaX
deltaZ 属性。
deltaY
deltaZ 属性。
deltaZ
初始化 deltaZ 属性于该 WheelEvent 对象上。该属性的相对正值(以及 deltaXdeltaY 属性)由右手坐标系给出,其中 X、Y 与 Z 轴的正方向分别指向文档的最右边缘、最下边缘与最远深度(远离用户)。 相对负值则分别指向相反方向。
deltaMode
deltaMode 属性初始化为枚举值 0、 1 或 2,这些值 分别表示滚动的像素量 (DOM_DELTA_PIXEL)、滚动的行数 (DOM_DELTA_LINE)或滚动的页数 (DOM_DELTA_PAGE),如果该滚轮的旋转本会导致滚动。

6.2 滚轮事件类型

6.2.1 wheel

Type wheel
Interface WheelEvent
Sync / Async 异步
Bubbles
Trusted Targets Element
Cancelable 视情况而定
Composed
Default action 滚动(或缩放)文档
Context
(trusted events)
  • Event.target : 当前滚轮事件事务的元素目标
  • UIEvent.view : Window
  • UIEvent.detail : 0
  • MouseEvent.screenX : 如果该滚轮与某指向设备相关联,则该值基于屏幕上的指针位置;否则为 0
  • MouseEvent.screenY : 如果该滚轮与某指向设备相关联,则该值基于屏幕上的指针位置;否则为 0
  • MouseEvent.clientX : 如果该滚轮与某指向设备相关联,则该值基于视口内的指针位置;否则为 0
  • MouseEvent.clientY : 如果该滚轮与某指向设备相关联,则该值基于视口内的指针位置;否则为 0
  • MouseEvent.altKey : 若 Alt 修饰键处于激活状态则为 true, 否则为 false
  • MouseEvent.ctrlKey : 若 Control 修饰键处于激活状态则为 true, 否则为 false
  • MouseEvent.shiftKey : 若 Shift 修饰键处于激活状态则为 true, 否则为 false
  • MouseEvent.metaKey : 若 Meta 修饰键处于激活状态则为 true, 否则为 false
  • MouseEvent.button : 如果滚轮与某指向设备相关联,则该值基于当前按下的按钮;否则为 0
  • MouseEvent.buttons : 如果滚轮与某指向设备相关联,则该值基于当前按下的全部按钮;若没有按下任何按钮则为 0
  • MouseEvent.relatedTarget : 指示指向设备所指向的目标(若存在)
  • WheelEvent.deltaX : 按 deltaMode 单位,页面沿 x 轴预计将滚动的量;或实现特定的滚轮绕 x 轴的移动值
  • WheelEvent.deltaY : 按 deltaMode 单位,页面沿 y 轴预计将滚动的量;或实现特定的滚轮绕 y 轴的移动值
  • WheelEvent.deltaZ : 按 deltaMode 单位,页面沿 z 轴预计将滚动的量;或实现特定的滚轮绕 z 轴的移动值
  • WheelEvent.deltaMode : deltaX、deltaY 与 deltaZ 属性的单位指示器(像素、行或页)
用户代理 MUST 当鼠标滚轮绕任一轴发生旋转时,或当等价的输入设备 (例如鼠标球、某些数位板或触控板等)模拟了此类动作时, 派发该事件。取决于平台与输入设备, 斜向滚轮 delta MAY 以单个 wheel 事件(具有多个非零轴)交付,或以每个非零轴各自一个的独立 wheel 事件交付。 wheel 事件类型的典型默认动作是 按所指示的量滚动(或在某些情况下缩放)文档。 如果该事件被取消,实现 MUST NOT 滚动或缩放文档(或执行与该事件类型相关联的任何其他 实现特定的默认动作)。
注意

在某些 用户 代理中,或在某些输入设备上, 转动滚轮的速度可能会影响 delta 值, 速度越快产生的 delta 值越高。

6.2.2 滚轮事件的可取消性

在滚轮事件上调用 preventDefault 可以阻止 或以其他方式中断滚动。为获得最佳滚动性能,用户代理可能不会等待与滚动相关联的每个滚轮事件 都被处理后再判断其是否会被取消。 在这种情况下,用户代理应生成 cancelable 属性为 falsewheel 事件, 以表示无法使用 preventDefault 来阻止或中断 滚动。否则 cancelable 将为 true

特别地,当用户代理 观察到该事件没有任何非被动监听器时,它应当仅生成不可取消的 wheel 事件。

7. Element 接口的扩展

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

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

为参数 pointerId 所标识的指针,在调用该方法的元素上设置指针捕获。对该指针的后续事件,捕获目标将替代正常的命中测试结果(仿佛指针始终位于捕获目标之上),并且这些事件 MUST 始终以该元素为目标,直到捕获被释放为止。为使此方法生效,该指针 MUST 处于其活动按钮状态;否则将静默失败。当提供的方法参数与任何活动指针都不匹配时,抛出一个“NotFoundErrorDOMException

releasePointerCapture()

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

hasPointerCapture

指示调用该方法的元素是否对参数 pointerId 所标识的指针拥有指针 捕获。具体而言:如果 待定指针捕获目标覆盖(针对 pointerId)被设置为调用该方法的元素,则返回 true;否则返回 false

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

8. GlobalEventHandlers mixin 的扩展

以下章节描述了对现有 GlobalEventHandlers mixin 的扩展,以便于事件处理器的注册。

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
用于 pointerover 事件类型的 事件处理器 IDL 属性
onpointerenter
用于 pointerenter 事件类型的 事件处理器 IDL 属性
onpointerdown
用于 pointerdown 事件类型的 事件处理器 IDL 属性
onpointermove
用于 pointermove 事件类型的 事件处理器 IDL 属性
onpointerrawupdate
用于 pointerrawupdate 事件类型的 事件处理器 IDL 属性
onpointerup
用于 pointerup 事件类型的 事件处理器 IDL 属性
onpointercancel
用于 pointercancel 事件类型的 事件处理器 IDL 属性
onpointerout
用于 pointerout 事件类型的 事件处理器 IDL 属性
onpointerleave
用于 pointerleave 事件类型的 事件处理器 IDL 属性
ongotpointercapture
用于 gotpointercapture 事件类型的 事件处理器 IDL 属性
onlostpointercapture
用于 lostpointercapture 事件类型的 事件处理器 IDL 属性

9. Navigator 接口的扩展

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

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

getter 步骤如下:

  1. emulated maxTouchPointsWebDriver BiDi 模拟的最大触点数 [WEBDRIVER-BIDI] 的结果。
  2. 如果 emulated maxTouchPoints 不为 null,则返回 emulated maxTouchPoints
  3. 返回设备所支持的同时触摸接触点的最大数量。对于具有多个数字化仪的设备(例如 多个触摸屏),该值 MUST 为各个数字化仪分别所支持的最大接触点数集合中的最大值。

    例如,假设某设备有 3 个触摸屏,它们分别支持 2、5 与 10 个同时触摸接触点。那么 maxTouchPoints 的值应为 10

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

10. 声明直接操控行为

属性与默认动作 中所述,视口 操作(平移与缩放)无法通过取消指针事件来抑制。相反,作者必须使用 touch-action CSS 属性以声明式方式定义他们希望允许哪些行为,以及希望抑制哪些行为。

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

10.1 touch-action CSS 属性

Name: touch-action
Value: auto | none | [ [ pan-x | pan-left | pan-right ] || [ pan-y | pan-up | pan-down ] ] | manipulation
Initial: auto
Applies to: 除以下之外的所有元素:非替换的内联元素、表格行、行组、表格列 与列组
Inherited:
Percentages: N/A
Media: visual
Computed value: 与指定值相同
Canonical order: 按语法
Animation type: 不可动画

touch-action CSS 属性用于确定直接 操控交互(尽管属性名如此,它并不局限于触摸)是否 MAY 触发用户代理的平移与缩放行为。参见 touch-action章节。

在开始平移或缩放之前,用户代理 MUST 在满足以下所有条件时抑制一个指针事件流

注意
某些用户代理实现了涉及一系列彼此分离的离散手势的复杂手势行为, 但这些手势都被视为单一连续手势的一部分。例如,考虑触屏上的“甩动以滚动(fling to scroll)” 手势:用户以快速的手指移动开始平移文档,将手指从触屏抬起,文档在模拟惯性的作用下继续 平移。当文档仍在移动时,用户可能再次将手指放到触屏上并执行另一次“甩动”,为平移提供 进一步动量,或对抗当前平移以减速、完全停止平移,或反转平移方向。由于本规范并未以 规范性方式定义手势与行为如何实现,因此由用户代理自行决定第二次触摸(在其被解释为第二次 “甩动”或对当前平移的对抗之前)是否触发指针事件。
注意
touch-action 不会应用/级联到嵌入式浏览上下文中。 例如,即使将 touch-action 应用于 <iframe>, 也不会对 <iframe> 本身内部用于平移与缩放的直接操控交互行为产生任何影响。

10.2 确定所支持的直接操控行为

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

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

10.3 touch-action 值的细节

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

注意
术语 “panning” 与 “scrolling” 被视为同义(或更准确地说,“panning” 是使用直接操控输入的 “scrolling”)。 定义用于触发平移/滚动的交互或手势,或定义用于触发 autonone 值对应行为的方法,均不在本规范范围内。
auto
用户代理 MAY 考虑任何被允许、且在元素上开始的与视口平移与缩放相关的直接操控行为。
none
在元素上开始的直接操控交互 MUST NOT 触发与视口平移与缩放相关的行为。
pan-x
pan-left
pan-right
pan-y
pan-up
pan-down
用户代理 MAY 仅为平移目的考虑在元素上开始的直接操控交互,且该平移必须以所列全部值指定的任一方向开始。 一旦平移开始,即使反向开始的平移被禁止,用户也可以反转方向。 相比之下,当平移被限制在单一轴上(例如使用 pan-xpan-y)时,在平移期间不能更改轴向。
manipulation
用户代理 MAY 仅为平移与连续缩放(例如捏合缩放)目的考虑在元素上开始的直接操控交互,但 MUST NOT 触发其他相关行为,这些行为依赖于必须在设定时间内发生的多次激活(例如双击缩放,或双击并按住以进行单指缩放)。
注意
实现中常见的额外 touch-action定义于 [COMPAT]。
注意
touch-action 属性仅适用于同时支持 CSS widthheight 属性的元素(见 [CSS21])。 该限制旨在便于用户代理对 低延迟直接操控平移与缩放进行优化。 对于默认不支持的元素,例如作为非替换内联元素<span>, 作者可以将 display CSS 属性设置为支持 widthheight 的值,例如 block。未来的规范可以将此 API 扩展到所有元素。
注意

方向特定的 pan 值可用于自定义某些回弹滚动(overscroll)行为。 例如,为实现简单的下拉刷新效果,当滚动位置为 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 将移除此延迟。 注意,用于判断单击或双击手势的方法不在本规范范围内。
Example 9: 禁止 所有直接操控行为
<div style="touch-action: none;">
    该元素会为所有原本会导致平移或缩放的直接操控交互接收指针事件。
</div>
Example 10: 仅允许 水平平移
<div style="touch-action: pan-x;">
    当不在水平方向平移时,该元素会接收指针事件。
</div>
Example 11: 禁止用于平移与缩放的直接操控行为的子区域
<div style="overflow: auto;">
    <div style="touch-action: none;">
        该元素会为所有原本会导致平移或缩放的直接操控交互接收指针事件。
    </div>
    <div>
        在该元素上的直接操控交互 MAY 被用于操控父元素。
    </div>
</div>
示例 12: 禁止用于平移与缩放的直接操控行为的中间父元素
<div style="overflow: auto;">
    <div style="touch-action: pan-y;">
        <div style="touch-action: pan-x;">
            该元素会为所有直接操控交互接收指针事件,因为
            它仅允许水平平移,但其某个中间祖先
            (它与可滚动元素之间)仅允许垂直平移。
            因此,用户代理不会处理任何用于平移/缩放的直接操控行为。
        </div>
    </div>
</div>
Example 13: 限制用于平移与缩放的允许的直接操控行为的中间父元素
<div style="overflow: auto;">
    <div style="touch-action: pan-y pan-left;">
        <div style="touch-action: pan-x;">
            当不向左平移时,该元素会接收指针事件。
        </div>
    </div>
</div>

11. 指针 捕获

11.1 介绍

本节为非规范性内容。

指针捕获允许将某个特定指针的事件(包括任何 兼容鼠标事件)重新定向到某个 特定元素,而不是按该指针位置的正常 命中测试结果来确定目标。 这在诸如自定义滑块控件之类的场景中很有用(例如类似于 [HTML] <input type="range"> 控件)。可以在滑块的滑块柄(thumb) 元素上设置指针捕获,使用户即使指针滑出滑块柄 之外也能来回滑动该控件。

自定义音量滑块
7 自定义滑块控件示例:通过来回滑动滑块柄 元素来选择一个值。在滑块柄上触发 pointerdown 之后,可以使用指针捕获 使用户即使指针漂移离开它也能继续滑动滑块柄。

11.2 设置指针捕获

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

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

11.3 释放指针捕获

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

  1. 如果作为该方法参数提供的 pointerId 与任何 活动指针都不匹配,且这些步骤并非因指针捕获的隐式释放而被调用,则 抛出一个“NotFoundErrorDOMException
  2. 如果对于具有指定 pointerIdElementhasPointerCapture 为 false,则终止这些步骤。
  3. 对于指定的 pointerId,清除(若已设置)待定指针捕获目标覆盖
注意
参见 设置指针捕获章节中的注意事项。

11.4 隐式指针捕获

实现用于平移与缩放的 直接操控 交互的输入 (例如触屏上的触摸或触控笔)SHOULD 其行为应当与在调用任意 pointerdown 监听器之前、刚好在目标元素上调用了 setPointerCapture 完全一致。可使用 hasPointerCapture API(例如在 pointerdown 监听器中)来判断这是否发生过。如果在触发下一次指针事件之前,未为该指针调用 releasePointerCapture,则会向目标(按常规方式)派发一个 gotpointercapture 事件,以表明捕获处于激活状态。

注意
这相对于 [PointerEvents] 是一项破坏性变更,但对绝大多数既有内容没有影响。 除了匹配典型的平台 UX 约定之外,这种隐式捕获设计还使用户代理能够进行一种性能优化:在没有开发者显式选择加入的情况下, 可以避免在触摸移动事件上执行命中测试(与现有主流原生与 Web 触摸输入 API 的性能特性一致)。
注意
此外,用户代理也可能在特定 UI 小部件(例如 input range 控件)上为所有输入设备实现隐式指针捕获行为 (允许在交互期间手指的某些移动偏离表单控件本身)。

11.5 指针捕获的隐式释放

在触发 pointeruppointercancel 事件之后,立即 用户代理 MUST 为刚刚派发的 pointeruppointercancel 事件的 pointerId 清除 待定指针捕获目标覆盖, 然后执行 处理待定指针捕获 步骤,以便在必要时触发 lostpointercapture。 在执行 处理待定指针捕获 步骤之后, 若该指针支持悬停,用户代理 MUST 还必须发送相应的边界事件, 以反映当前指针在无捕获情况下的位置。

指针捕获目标覆盖不再连接 [DOM] 时, 指针捕获目标覆盖 SHOULD 被设置为 document。

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

注意
上述两段会导致一个与被捕获指针对应的 lostpointercapture 事件 在捕获节点被移除之后的下一次 处理待定指针捕获期间 在 document 上触发。

当某个元素成功应用了指针锁定 [PointerLock] 时,如果任何元素被设置为已捕获或待捕获, 用户代理 MUST 执行步骤,仿佛已调用了 releasePointerCapture 方法。

12. 合并事件与预测事件

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

12.1 合并事件

出于性能原因,用户代理可能选择不在每次指针的某个可测量属性 (例如坐标、压力、切向压力、倾斜、旋转或接触几何) 更新时都发送一个 pointermove 事件。相反,它们可能将多次变化合并(combine/merge)为 单个 pointermovepointerrawupdate 事件。虽然 这种方式有助于减少 用户代理 MUST 执行的事件处理量, 但它自然会降低跟踪指针位置时的粒度与保真度, 特别是在快速且幅度很大的移动情况下。通过使用 getCoalescedEvents 方法, 应用可以访问原始的、未被合并的位置变化。这些 使得对指针移动数据的处理更精确。例如在绘图 应用中,未合并事件可用于绘制更平滑的曲线, 更接近指针的实际移动。

曲线的局部放大视图,显示合并点与未合并点
8 绘图应用中的一条曲线示例——仅使用 pointermove 事件中的合并 坐标(灰点)时,曲线明显更 棱角分明且锯齿状;使用 getCoalescedEvents() 提供的更细粒度点(红圈)绘制的同一条线,会得到对指针移动更平滑的近似。

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

注意
由于可信的父事件是合并事件的摘要或聚合, 开发者通常只需要处理父事件 或处理全部合并事件之一,而不需要两者都处理。
注意
当包含合并事件列表的可信事件从 JavaScript 重新派发时, 事件派发算法会将该事件的 isTrusted 位设置为 false, 但合并事件列表中对应的位仍保持其原始 true 值不变。

可信事件的合并事件列表中的事件将具有:

Example 14: 使用合并事件列表的基础 canvas 绘图应用
<style>
    /* 禁用用户代理内建的直接操控默认行为(例如平移或缩放)
    使得在 canvas 元素上的所有事件都会交由应用处理。 */

    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 的属性将以最能代表合并事件列表中 事件的方式进行初始化。用户代理应如何做到这一点的具体方法不在 本规范范围内。

所有这些已派发事件的顺序 MUST 与 原始事件的实际顺序一致。 例如,如果某个 pointerdown 事件导致派发合并的 pointermove 事件, 用户代理 MUST 先派发一个 pointermove 事件(其包含该 pointerId 的全部合并事件),再派发 pointerdown 事件。

注意

以下是一个示例,展示了实际发生的事件(其 timeStamp 值递增) 以及用户代理派发的事件:

实际事件 已派发事件
指针(pointerId=2) 坐标变化 pointerrawupdatepointerId=2)含 1 个 合并事件
指针(pointerId=1) 坐标变化 pointerrawupdatepointerId=1)含 1 个 合并事件
指针(pointerId=2) 坐标变化 pointerrawupdatepointerId=2)含 1 个 合并事件
指针(pointerId=2) 坐标变化 pointerrawupdatepointerId=2)含 1 个 合并事件
指针(pointerId=1) 坐标变化 pointerrawupdatepointerId=1)含 1 个 合并事件
指针(pointerId=2) 坐标变化 pointerrawupdatepointerId=2)含 1 个 合并事件
指针(pointerId=1)按钮按下 pointermovepointerId=1)含 2 个 合并事件
pointermovepointerId=2)含 4 个 合并事件
pointerdownpointerId=1)含 0 个 合并事件
指针(pointerId=2) 坐标变化 pointerrawupdatepointerId=2)含 1 个 合并事件
指针(pointerId=2) 坐标变化 pointerrawupdatepointerId=2)含 1 个 合并事件
指针(pointerId=1)按钮释放 pointermovepointerId=2)含 2 个 合并事件
pointeruppointerId=1)含 0 个 合并事件

12.2 预测事件

一些用户代理内建算法会在一系列已确认的指针移动之后, 根据当前手势的先前事件以及移动的速度/轨迹,对未来指针移动的位置进行预测。 应用可使用 getPredictedEvents 方法 利用该信息,以推测方式向预测位置“提前绘制”,从而降低感知到的延迟, 并在收到实际点位后丢弃这些预测点位。

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

PointerEvent 具有一个关联的 预测事件列表(一个包含零个或多个 PointerEvent 的列表)。对于可信 pointermove 事件,它是一系列用户代理预测将在未来跟随该事件发生的 PointerEvent。对于所有其他可信事件类型,该列表为空列表。 不可信事件的预测事件列表会被初始化为传给构造函数的值。

注意

虽然 pointerrawupdate 事件可能具有非空的合并事件列表, 但出于性能原因,它们的预测事件列表通常会是空列表。

注意
当包含预测事件列表的可信事件从 JavaScript 重新派发时, 事件派发算法会将该事件的 isTrusted 位设置为 false, 但预测事件列表中对应的位仍保持其原始 true 值不变。

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

可信事件的预测事件列表中的事件将具有:

注意

注意,作者只应在下一个指针事件被派发之前,将预测事件视为有效预测。 根据用户代理预测事件的未来程度,有可能常规 指针事件会早于一个或多个预测事件的时间戳被派发。

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

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

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

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

  1. 将该事件的 pointerId pointerTypeisPrimaryisTrusted 设置为与“父”指针事件的相应属性一致。
  2. 将该事件的 cancelablebubbles 设置为 false(因为这些事件永远不会被单独派发)。
  3. 将该事件的合并事件列表预测事件列表设置为空列表。
  4. 将所有其他属性初始化为默认的 PointerEvent 值。

当可信 PointerEventtarget 发生改变时,用户代理 SHOULD合并事件列表预测事件列表中的每个事件:

  1. 将该事件的 target 设置为与“父”指针事件的 target 一致。

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

当今现存的绝大多数 Web 内容仅针对鼠标事件(Mouse Events)进行编程。以下描述了一个 算法,用于说明 用户 代理 MAY 如何将通用指针输入映射为鼠标事件,以便与 这些内容兼容。

与鼠标事件的兼容性映射是本规范的一个 可选(OPTIONAL)特性。 为了与现有遗留内容获得最佳兼容性,鼓励用户代理支持该特性。

注意

从高层次来看,兼容鼠标事件旨在与其各自的 指针事件进行“交错(interleaved)”派发。然而,这一特定顺序并非强制要求, 实现兼容鼠标事件的用户代理 MAY 决定延迟或分组派发 鼠标事件,只要它们的相对顺序保持一致即可。

特别是在触摸屏输入的情况下,用户代理 MAY 应用额外的手势识别启发式策略 (除非作者通过 touch-action 显式抑制)。 在从 pointerdown 事件到 pointerup 事件之间的一系列事件期间, 手势识别可能必须等到 pointerup 事件时才能检测或忽略某个手势。 因此,如果用户代理判定某次交互并非意图作为某个特定手势,则整个序列的兼容鼠标事件 可能会在最后一个 pointerup 事件之后一起派发。 用户代理的这些手势识别细节并未在本规范中定义,并且在不同实现之间可能有所不同。

无论是否支持兼容鼠标事件,用户代理 MUST 始终支持 clickauxclickcontextmenu 事件,因为 这些事件的类型为 PointerEvent,因此它们不是 兼容鼠标事件。 在指针事件期间调用 preventDefault MUST NOT 影响 clickauxclickcontextmenu 是否触发。

注意

某些高层事件(例如 contextmenufocusblur)与指针 事件的相对顺序未定义,并且在不同用户代理之间会有所差异。例如,在一些用户代理中, contextmenu 往往会紧随 pointerup 之后触发,而在另一些用户代理中,它往往会先于 pointeruppointercancel 触发;并且在某些情况下,它可能在没有任何对应指针事件的情况下触发 (例如,由键盘交互导致)。

此外,用户代理可能会应用其自身的启发式策略来确定是否应触发 clickauxclickcontextmenu 事件。 一些用户代理可能会选择不触发这些事件,如果存在同类型的其他(非主)指针,或存在不同类型的其他主指针。 用户代理可能会判定某个动作并不是一次“干净”的轻触、点击或长按(例如,如果触摸屏上手指的交互在保持接触屏幕期间包含过多移动), 并据此决定不触发 clickauxclickcontextmenu 事件。 用户代理行为的这些方面并未在本规范中定义,并且在不同实现之间可能有所不同。

除非另有说明,任何被映射的鼠标事件的目标 SHOULD 与其对应的指针事件具有相同的目标, 除非该目标不再参与其 ownerDocument 的树。在这种情况下,应当在原始目标的、仍参与其 ownerDocument 树的最近祖先节点(在其从树中移除时)上触发该鼠标事件, 这意味着会为该鼠标事件构建一条新的事件路径(基于新的目标节点)。

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

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

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

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

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

虽然只有 主指针可以产生兼容鼠标事件,但 多个主指针可以同时处于活动状态,并且每个都会产生自己的兼容鼠标事件。 为了与依赖 MouseEvents 的脚本兼容,鼠标过渡事件(mouseovermouseoutmouseentermouseleaveSHOULD 模拟单个传统鼠标输入的移动。 这意味着每个事件目标的进入/退出状态都是有效的,符合 [UIEVENTS]。 用户代理 SHOULD 通过在文档中维护 传统鼠标指针的有效位置 来保证这一点,如下所述。

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

  1. T 为正在派发的 pointerdownpointeruppointermove 事件的目标。 对于 pointerleave 事件,取消设置 T
  2. 如果 T 与当前的 传统鼠标指针的有效位置 都未设置,或者两者相等, 则终止这些步骤。
  3. 按 [UIEVENTS] 所述, 以鼠标从当前 传统鼠标指针的有效位置 移动到 T 为前提,派发 mouseovermouseoutmouseentermouseleave 事件。 将当前 传统鼠标指针的有效位置T 其中任意一个未设置的值视为窗口外的鼠标位置。
  4. 传统鼠标指针的有效位置 设置为 T
注意

传统鼠标指针的有效位置建模了这样一个事实:我们并不总能将指针过渡事件(pointeroverpointeroutpointerenterpointerleave)直接映射到对应的传统鼠标过渡事件(mouseovermouseoutmouseentermouseleave)。 以下动画展示了一个案例:为了能够使用单一传统鼠标输入来协调两个主指针,用户代理需要派发比指针过渡事件更多的传统鼠标过渡事件。

Figure 10 同时存在的鼠标指针(白色光标)与触摸指针(白色“手”形光标) 会导致单一传统鼠标输入 (橙色光标)在两个指针之间移动。

在该动画中,请注意鼠标点击与触摸轻触之间的时间段。按钮 1 没有收到 pointerout 事件(因为“真实”的鼠标指针在此期间并未离开按钮矩形), 但当触摸轻触导致传统鼠标指针的有效位置移动到按钮 2 时, 按钮 1 会收到一个 mouseout 事件。类似地,在触摸轻触与鼠标离开按钮 1 之前的那段时间里, 按钮 1 出于同样原因没有收到 pointerover 事件, 但当传统鼠标指针的有效位置移回按钮 1 内部时, 按钮 1 会收到一个 mouseover 事件。

13.2 对支持悬停的设备的映射

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

  1. 如果将要派发的指针事件的 isPrimary 属性为 false, 则派发该指针事件并终止这些步骤。
  2. 如果将要派发的指针事件是 pointerdownpointeruppointermove 事件, 或在 window 上的 pointerleave 事件, 则按 跟踪传统鼠标指针的有效位置 中所述派发兼容鼠标过渡事件。
  3. 派发该指针事件。
  4. 如果所派发的指针事件为 pointerdown,并且该事件的 canceled flag 被设置,则为该 pointerType 设置 PREVENT MOUSE EVENT 标志。
  5. 如果该 pointerTypePREVENT MOUSE EVENT 标志设置,且所派发的指针事件为:
  6. 如果所派发的指针事件是 pointeruppointercancel,则清除该 pointerTypePREVENT MOUSE EVENT 标志。

13.3 对不支持悬停的设备的映射

某些设备(例如大多数触摸屏)不支持在非活动状态下悬停某个坐标(或一组坐标)。 大量针对鼠标事件编写的既有内容假定由鼠标产生事件,因此通常具有以下特征:

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

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

  1. 如果将要派发的指针事件的 isPrimary 属性为 false, 则派发该指针事件并终止这些步骤。
  2. 如果将要派发的指针事件是 pointerover,并且尚未为该指针派发 pointerdown 事件,则触发一个 mousemove 事件 (为了与遗留的鼠标特定代码兼容)。
  3. 如果将要派发的指针事件是 pointerdownpointeruppointermove 事件, 或在 window 上的 pointerleave 事件, 则按 跟踪传统鼠标指针的有效位置 中所述派发兼容鼠标过渡事件。
  4. 派发该指针事件。
  5. 如果所派发的指针事件为 pointerdown,并且该事件的 canceled flag 被设置,则为该 pointerType 设置 PREVENT MOUSE EVENT 标志。
  6. 如果该 pointerTypePREVENT MOUSE EVENT 标志设置,且所派发的指针事件为:
  7. 如果所派发的指针事件是 pointeruppointercancel,则清除该 pointerTypePREVENT MOUSE EVENT 标志。

如果用户代理同时支持触摸事件(如 [TOUCH-EVENTS] 中定义的)与指针事件,则 用户 代理 MUST NOT 同时生成本节所述的兼容鼠标事件,以及 [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 事件的 canceled flag 被设置,则事件序列将为:

  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

14. 安全与隐私 注意事项

本附录讨论指针事件(Pointer Events)实现的安全与隐私注意事项。讨论仅限于 由本规范所定义的事件模型、API 与事件的实现直接引发的安全与隐私问题。

本规范中定义的许多事件类型会因用户操作而被派发。这使得恶意的事件监听器能够获取 用户通常认为是机密的信息,例如:用户在与页面交互时,其鼠标/触控笔/手指的精确路径/移动轨迹。

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

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

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

本规范定义了作者可以访问“预测事件(predicted events)”的方法。本规范本身 并未定义用户代理应使用的预测算法。规范作者设想这些算法仅依赖于与用户正在执行的当前手势相关的先前指针事件。 用户代理有责任确保其预测算法的具体实现不会依赖任何额外数据——例如用户跨不同站点的完整交互历史—— 因为这些数据可能泄露用户的敏感信息,或被用于对其进行“指纹识别”并跟踪。

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

15. 术语表

本节为非规范性内容。

活动 按钮状态
指针的 buttons 属性具有非零值时的状态。对于鼠标,这是指设备至少有一个按钮处于按下状态。 对于触摸,这是指与数字化仪发生物理接触。对于笔,这是指笔与数字化仪发生物理接触,或在悬停时至少有一个按钮处于按下状态。
活动 文档
对于每个 活动指针,接收该指针最后一次事件的文档。
活动指针
任何触摸接触点、笔/触控笔、鼠标光标或其他可产生事件的指针。如果某个给定指针(由唯一的 pointerId 标识) 仍可能在该文档内产生额外事件,则该指针仍被视为活动。示例:
  • 连接到设备的鼠标始终是活动的。
  • 屏幕上的一次触摸接触点被视为活动。
  • 如果触摸接触点或笔/触控笔被抬起并超出数字化仪的范围,则它不再被视为活动。
注意
在某些平台上,活动指针的集合包括设备上的所有指针输入, 包括任何未以用户代理为目标的输入(例如以其他应用为目标的输入)。
接触 几何形状
数字化仪上某个输入(最常见的是触摸)的边界框。它通常指 指针输入分辨率粗于单个像素的设备。一些设备完全不报告该数据。
delta
用户代理将响应支持 WheelEvent 接口的输入设备(例如鼠标滚轮或触控板)的物理移动, 而对页面执行滚动或缩放的估计量(以像素、行或页为单位)。 delta 的值(例如 deltaXdeltaYdeltaZ 属性)应当在当前 deltaMode 属性的语境中解释。 滚轮(或其他设备)的物理移动与 delta 为正或为负之间的关系取决于环境与设备。 然而,如果用户代理将滚动作为 默认动作, 则 delta 的符号由右手坐标系给出, 其中正的 X、Y 与 Z 轴分别指向 文档 的最右边缘、最下边缘与最远的深度(远离用户)。
数字化仪
一种输入感测设备,其表面能够检测到接触和/或近距离的输入。最常见的是用于感测触摸接触点或笔/触控笔输入的表面。
直接 操控
某些用户代理(例如触屏设备上的浏览器)实现了一种“直接操控(direct manipulation)”隐喻,其中指针不仅与控件交互, 还被用于直接平移或缩放当前页面,从而提供直接物理接触的错觉。 例如,在触屏设备上,用户通常可以使用手指或触控笔“抓住”页面,并通过移动指针来平移页面,从而直接操控页面。 与之对比,在常规桌面/笔记本上的鼠标指针,平移是通过滚动条完成的,而不是通过“拖动”页面完成。
注意
在某些情况下,触控板(例如笔记本上的触控板)允许用户通过在触控板上“拖动”来滚动。 然而,这通常是通过触控板生成“伪造”的鼠标滚轮事件来实现的,因此这不算作直接操控。
滞后
人机界面设计中的一种特性:为改善用户体验,在某个位置或时间范围内接受输入值。 例如,允许用户双击鼠标按钮所需时间出现小幅偏差属于时间滞后; 在用户从父窗口移动到子菜单时,不会因为鼠标移出父窗口就立即关闭嵌套菜单属于位置滞后。
可测量属性

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

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

指针
一种与硬件无关的输入设备表示形式,这些输入设备能够指向屏幕上的特定坐标(或一组坐标),例如鼠标、笔或触摸接触点。
旋转
使用 WheelEvent 接口的输入设备上的增量变化指示。 在某些设备上,这 MAY 是滚轮的字面旋转;而在其他设备上,它 MAY 是沿平坦表面的移动,或对某个特定按钮的按压。
最上层事件 目标
最上层事件目标 MUST 是渲染顺序中最高的元素, 且该元素能够成为一个目标。 在图形用户界面中,这是用户指向设备下方的元素。用户界面的 命中测试 设施用于确定目标。 关于命中测试与层叠顺序的具体细节,请参见宿主语言

16. 遗留事件初始化器

本节为规范性内容。 以下特性已废弃,仅应由需要与遗留软件兼容的 用户代理实现。 另请参见 [UIEvents] 中的 遗留事件初始化器

16.1 接口 MouseEvent 的初始化器

WebIDLpartial interface MouseEvent {
	// Deprecated in this specification
	undefined initMouseEvent(DOMString typeArg,
		optional boolean bubblesArg = false,
		optional boolean cancelableArg = false,
		optional Window? viewArg = null,
		optional long detailArg = 0,
		optional long screenXArg = 0,
		optional long screenYArg = 0,
		optional long clientXArg = 0,
		optional long clientYArg = 0,
		optional boolean ctrlKeyArg = false,
		optional boolean altKeyArg = false,
		optional boolean shiftKeyArg = false,
		optional boolean metaKeyArg = false,
		optional short buttonArg = 0,
		optional EventTarget? relatedTargetArg = null);
};
initMouseEvent(typeArg)
初始化 MouseEvent 对象的属性。 该方法的行为与 UIEvent.initUIEvent() 相同。
警告

initMouseEvent 方法已废弃,但为了与广泛部署的实现保持向后兼容而仍被支持。

DOMString typeArg
有关该参数的说明,请参见 initEvent() 方法。
boolean bubblesArg
有关该参数的说明,请参见 initEvent() 方法。
boolean cancelableArg
有关该参数的说明,请参见 initEvent() 方法。
Window? viewArg
指定 view。 该值 MAYnull
long detailArg
指定 detail
long screenXArg
指定 screenX
long screenYArg
指定 screenY
long clientXArg
指定 clientX
long clientYArg
指定 clientY
boolean ctrlKeyArg
指定 ctrlKey
boolean altKeyArg
指定 altKey
boolean shiftKeyArg
指定 shiftKey
boolean metaKeyArg
指定 metaKey
short buttonArg
指定 button
EventTarget? relatedTargetArg
指定 relatedTarget。该值 MAYnull

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.

感谢过去负责鼠标与滚轮事件的诸位: Gary Kacmarcik, Travis Leithead,以及 多年来的各位 贡献者

特别感谢帮助开创本模型第一版的各位,尤其包括:Charu Chandiram、Peter Freiling、Nathan Furtwangler、Thomas Olsen、Matt Rakow、Ramu Ramanathan、Justin Rogers、 Jacob Rossi、Reed Townsend 以及 Steve Wright。

B. 修订历史

本节为非规范性内容。

以下为本规范各次发布之间,相对于 [PointerEvents3] 规范而言, 重大与重要编辑变更的说明性摘要。 参见 本规范编辑草案的完整修订历史

C. IDL 索引

WebIDL[Exposed=Window]
interface MouseEvent : UIEvent {
	constructor(DOMString type, optional MouseEventInit eventInitDict = {});
	readonly attribute long screenX;
	readonly attribute long screenY;
	readonly attribute long clientX;
	readonly attribute long clientY;
	readonly attribute long layerX;
	readonly attribute long layerY;

	readonly attribute boolean ctrlKey;
	readonly attribute boolean shiftKey;
	readonly attribute boolean altKey;
	readonly attribute boolean metaKey;

	readonly attribute short button;
	readonly attribute unsigned short buttons;

	readonly attribute EventTarget? relatedTarget;

	boolean getModifierState(DOMString keyArg);
};

dictionary MouseEventInit : EventModifierInit {
	long screenX = 0;
	long screenY = 0;
	long clientX = 0;
	long clientY = 0;

	short button = 0;
	unsigned short buttons = 0;
	EventTarget? relatedTarget = null;
};

dictionary PointerEventInit : MouseEventInit {
    long        pointerId = 0;
    double      width = 1;
    double      height = 1;
    float       pressure = 0;
    float       tangentialPressure = 0;
    long        tiltX;
    long        tiltY;
    long        twist = 0;
    double      altitudeAngle;
    double      azimuthAngle;
    DOMString   pointerType = "";
    boolean     isPrimary = false;
    long        persistentDeviceId = 0;
    sequence<PointerEvent> coalescedEvents = [];
    sequence<PointerEvent> predictedEvents = [];
};

[Exposed=Window]
interface PointerEvent : MouseEvent {
    constructor(DOMString type, optional PointerEventInit eventInitDict = {});
    readonly        attribute long        pointerId;
    readonly        attribute double      width;
    readonly        attribute double      height;
    readonly        attribute float       pressure;
    readonly        attribute float       tangentialPressure;
    readonly        attribute long        tiltX;
    readonly        attribute long        tiltY;
    readonly        attribute long        twist;
    readonly        attribute double      altitudeAngle;
    readonly        attribute double      azimuthAngle;
    readonly        attribute DOMString   pointerType;
    readonly        attribute boolean     isPrimary;
    readonly        attribute long        persistentDeviceId;
    [SecureContext] sequence<PointerEvent> getCoalescedEvents();
    sequence<PointerEvent> getPredictedEvents();
};

[Exposed=Window]
interface WheelEvent : MouseEvent {
	constructor(DOMString type, optional WheelEventInit eventInitDict = {});
	// DeltaModeCode
	const unsigned long DOM_DELTA_PIXEL = 0x00;
	const unsigned long DOM_DELTA_LINE	= 0x01;
	const unsigned long DOM_DELTA_PAGE	= 0x02;

	readonly attribute double deltaX;
	readonly attribute double deltaY;
	readonly attribute double deltaZ;
	readonly attribute unsigned long deltaMode;
};

dictionary WheelEventInit : MouseEventInit {
	double deltaX = 0.0;
	double deltaY = 0.0;
	double deltaZ = 0.0;
	unsigned long deltaMode = 0;
};

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

partial interface MouseEvent {
	// Deprecated in this specification
	undefined initMouseEvent(DOMString typeArg,
		optional boolean bubblesArg = false,
		optional boolean cancelableArg = false,
		optional Window? viewArg = null,
		optional long detailArg = 0,
		optional long screenXArg = 0,
		optional long screenYArg = 0,
		optional long clientXArg = 0,
		optional long clientYArg = 0,
		optional boolean ctrlKeyArg = false,
		optional boolean altKeyArg = false,
		optional boolean shiftKeyArg = false,
		optional boolean metaKeyArg = false,
		optional short buttonArg = 0,
		optional EventTarget? relatedTargetArg = null);
};

D. 参考文献

D.1 规范性参考文献

[CSS-OVERFLOW-3]
CSS 溢出模块第 3 级。Elika Etemad;Florian Rivoal。W3C。2025 年 10 月 7 日。W3C 工作草案。URL: https://www.w3.org/TR/css-overflow-3/
[css-position]
CSS 定位布局模块第 3 级。Elika Etemad;Tab Atkins Jr。W3C。2025 年 10 月 7 日。W3C 工作草案。URL: https://www.w3.org/TR/css-position-3/
[CSS21]
层叠样式表 2 级修订 1(CSS 2.1)规范。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 视图模块。Simon Fraser;Emilio Cobos Álvarez。W3C。2025 年 9 月 16 日。W3C 工作草案。URL: https://www.w3.org/TR/cssom-view-1/
[DOM]
DOM 标准。Anne van Kesteren。WHATWG。现行标准。URL: https://dom.spec.whatwg.org/
[ECMASCRIPT]
ECMAScript 语言规范。 Ecma 国际。URL: https://tc39.es/ecma262/multipage/
[HTML]
HTML 标准。Anne van Kesteren; Domenic Denicola;Dominic Farolino;Ian Hickson;Philip Jägenstedt;Simon Pieters。WHATWG。现行标准。URL: https://html.spec.whatwg.org/multipage/
[HTML401]
HTML 4.01 规范。Dave Raggett; Arnaud Le Hors;Ian Jacobs。W3C。2018 年 3 月 27 日。W3C 推荐规范。URL: https://www.w3.org/TR/html401/
[infra]
Infra 标准。Anne van Kesteren;Domenic Denicola。WHATWG。现行标准。URL: https://infra.spec.whatwg.org/
[PointerEvents]
指针事件。Patrick Lauke; Robert Flack。W3C。2026 年 2 月 11 日。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]
用于 RFC 中指示需求级别的关键词。S. Bradner。IETF。1997 年 3 月。最佳当前实践。URL: https://www.rfc-editor.org/rfc/rfc2119
[RFC8174]
RFC 2119 关键词中字母大小写歧义。B. Leiba。IETF。2017 年 5 月。最佳当前实践。URL: https://www.rfc-editor.org/rfc/rfc8174
[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 事件。Xiaoqian Wu。W3C。2026 年 2 月 21 日。W3C 工作草案。URL: https://www.w3.org/TR/uievents/
[UIEvents-Key]
UI 事件 KeyboardEvent 键值参考。 Travis Leithead;Gary Kacmarcik。W3C。2025 年 4 月 22 日。W3C 推荐规范。URL: https://www.w3.org/TR/uievents-key/
[WEBDRIVER-BIDI]
WebDriver BiDi。James Graham;Alex Rudenko;Maksim Sadym。W3C。2026 年 2 月 25 日。W3C 工作草案。URL: https://www.w3.org/TR/webdriver-bidi/
[WEBIDL]
Web IDL 标准。Edgar Chen;Timothy Gu。 WHATWG。现行标准。URL: https://webidl.spec.whatwg.org/

D.2 说明性参考文献

[COMPAT]
Compatibility Standard. Mike Taylor. WHATWG. Living Standard. URL: https://compat.spec.whatwg.org/
[PointerEvents3]
Pointer Events. Patrick Lauke; Robert Flack. W3C. 13 February 2026. CRD. URL: https://www.w3.org/TR/pointerevents3/
[WCAG22]
Web Content Accessibility Guidelines (WCAG) 2.2. Michael Cooper; Andrew Kirkpatrick; Alastair Campbell; Rachael Bradley Montgomery; Charles Adams. W3C. 12 December 2024. W3C Recommendation. URL: https://www.w3.org/TR/WCAG22/