指针事件

第 4 级

W3C 工作草案

关于本文档的更多详细信息
此版本:
https://www.w3.org/TR/2026/WD-pointerevents4-20260616/
最新发布版本:
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. LaukeTetraLogical
Robert FlackGoogle
前任编辑:
Matt BrubeckMozilla
Rick ByersGoogle
Navid ZolghadrGoogle
反馈:
GitHub w3c/pointerevents拉取请求新建议题未解决议题
public-pointer-events@w3.org 主题行请写为 [pointerevents4] … 消息主题 …归档
浏览器支持:
caniuse.com

摘要

Pointer Events 规范定义了一种统一的、与硬件无关的框架,用于处理来自 各种设备的输入,包括鼠标、触摸屏以及笔/触控笔。通过提供一组单一的事件(例如 pointerdown、pointermove、pointerup),它允许开发者支持多样化的输入方式,而无需为每种设备编写 特定于设备的逻辑。

本规范还定义了 Mouse 和 Wheel Events,以及为其他 指针设备类型触发 Mouse Events 的映射。

本文档状态

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

本规范是对 [PointerEvents3] 规范的更新。它还 包括以前位于 [UIEVENTS] 规范中的 Mouse 和 Wheel Events。

本修订版包括新特性:

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

作为 工作草案发布并不表示 W3C 及其成员认可该文档。

这是一个草案文档,可能随时被其他 文档更新、取代或废弃。除作为进行中的工作之外, 引用本文档是不合适的。

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

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

1. 引言

本节为非规范性内容。

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

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

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

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

首先,鼓励作者通过响应诸如 focusblurclick 等高级事件,为所有形式的输入提供等效功能。 然而,在使用低级事件(例如 Pointer Events)时,鼓励作者 确保支持所有类型的输入。对于键盘和类似键盘的接口, 这可能需要额外添加显式的键盘事件处理。更多 细节请参见 Keyboard Accessible [WCAG22]。

指针输入结合了鼠标、笔和触摸等各种输入源
1 指针是一种与硬件无关的输入设备表示形式,它可以定位到屏幕上的 特定坐标(或一组坐标)。

用于处理通用指针输入的事件看起来非常像鼠标事件:pointerdownpointermovepointeruppointeroverpointerout,等等。这有助于将内容轻松 从 Mouse Events 迁移到 Pointer Events。 Pointer Events 提供 Mouse Events 中存在的所有常见属性(包括客户端坐标、 目标元素、按钮状态),此外还提供用于其他输入形式的新属性,例如压力、 接触几何形状、倾斜。作者可以很容易地针对 Pointer Events 编写代码,在有意义的地方 在不同输入类型之间共享逻辑,并且只在必要时为特定输入类型进行定制, 以获得最佳体验。

虽然 Pointer Events 来源于各种输入设备,但它们并未被定义为 由其他某组特定于设备的事件生成。虽然为了兼容性可以并且鼓励这样做,但本规范 不要求支持其他特定于设备的事件(例如鼠标事件或触摸事件)。用户代理 可以支持指针事件,而不支持任何其他设备事件。为了与 针对特定于鼠标的事件编写的内容兼容,本规范确实提供了一个可选章节,描述如何 根据来自鼠标以外设备的指针输入生成 兼容性鼠标事件

本规范不提供关于同时支持 Touch Events(如 [TOUCH-EVENTS] 中定义)和 Pointer Events 的用户代理预期行为的任何建议。 有关这两个规范之间关系的更多信息,请参见 Touch Events Community Group

2. 一致性

除标记为非规范性的章节外,本规范中的所有创作指南、图表、示例和注释 均为非规范性内容。除此之外,本规范中的所有内容均为规范性内容。

本文档中的关键词 MAYMUSTMUST NOTOPTIONALSHOULD 应按 BCP 14 [RFC2119] [RFC8174] 中所述进行解释,但仅限于它们如本文所示以全 大写形式出现时。

3. 示例

本节为非规范性内容。

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

示例 1:特性 检测和事件绑定
/* 绑定到 Pointer Events 或传统 touch/mouse */

if (window.PointerEvent) {
    // 如果支持 Pointer Events,则只监听 pointer 事件
    target.addEventListener("pointerdown", function(e) {
        // 如有必要,基于 e.pointerType 应用单独的逻辑
        // 用于不同的 touch/pen/mouse 行为
        ...
    });
    ...
} else {
    // 传统 touch/mouse 事件处理器
    target.addEventListener('touchstart', function(e) {
        // 阻止兼容性鼠标事件和 click
        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);
示例 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;
    // 检查是否存在唯一 Id。
    if (uniqueId == 0) {
        return;
    }
    // 检查是否已为该设备分配颜色。
    if (map.has(uniqueId)) {
        return;
    }
    // 为该设备分配颜色。
    let newColor = getNewColor();
    map.set(uniqueId, newColor);
    return newColor;
}

function getNewColor() {
    /* 返回某个颜色值 */
}
</script>

4. Mouse Events

鼠标事件模块源自 [HTML401] 的 onclickondblclickonmousedownonmouseuponmouseoveronmousemoveonmouseout 属性。此事件模块专门设计为 与指点输入设备一起使用,例如鼠标或轨迹球。

4.1 接口 MouseEvent

在 DOM Level 2 中引入,在本规范中修改。

MouseEvent 接口提供与 Mouse 事件关联的特定上下文 信息。

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

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

要创建 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 的值 MAY 用于 表示这些按钮。

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

某些与 默认动作相关的 事件,例如 mousedownmouseup,取决于正在使用的特定鼠标 按钮。

此属性的 未初始化值 MUST0

buttons

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

尽管名称相似,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 属性初始化为 MouseEvent 对象所需的 鼠标指针在用户屏幕上的水平相对位置。

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

screenY

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

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

clientX

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

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

clientY

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

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

button

button 属性初始化为 MouseEvent 对象中表示 鼠标按钮所需状态的数字。

值 0 用于表示主鼠标按钮,1 用于表示 辅助/中间鼠标按钮,2 用于表示右鼠标按钮。大于 2 的数字 也是可能的,但本文档未指定。

buttons

buttons 属性初始化为 MouseEvent 对象中表示 鼠标按钮中被认为处于活动状态的一个或多个按钮的数字。

buttons 属性是 位字段。如果掩码值 1 应用于该位字段的值时为 true,则 主鼠标按钮处于按下状态。如果掩码值 2 应用于该位字段的值 时为 true,则右鼠标按钮处于按下状态。如果掩码值 4 应用于 该位字段的值时为 true,则辅助/中间按钮处于按下状态。

在 JavaScript 中,要将 buttons 属性初始化为仿佛右键 (2) 和中键 (4) 被同时按下, buttons 值可以指定为:

{ buttons: 2 | 4 }

或:

{ buttons: 6 }
relatedTarget

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

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

4.2 MouseEvent 算法

4.2.1 原生 OS 要求

本节中的算法假定原生平台 OS 将提供以下内容:

  • 鼠标移动时的事件(由 处理 原生 mouse move 处理)
  • 鼠标按钮被按下时的事件(由 处理 原生 mouse down 处理)
  • 鼠标按钮被释放时的事件(由 处理 原生 mouse up 处理)
  • 一种识别鼠标按钮按下何时应解释为“点击”的方式(由 处理原生 mouse click 处理)
    • 例如,作为标志或作为单独事件
    • 如果触发单独的“click”事件,则原生 OS 将在 相应的“mouse up”事件之后立即触发它,期间没有其他与鼠标相关的事件
  • 一种识别鼠标点击何时为“双击”的方式(由 处理原生 mouse double click 处理)

对于这些事件,OS 将能够提供以下信息:

  • 相对于原生 OS 桌面的 x,y 鼠标坐标
  • 相对于 UA 窗口视口的 x,y 鼠标坐标
  • 当前按住了哪些键盘修饰键

4.2.2 构造 Mouse Events

警告

本节需要修订。

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

为了构造 MouseEvent,或使用以下算法从这些对象 派生出的对象,所有 MouseEvent 及其派生 对象都具有 内部键修饰键状态,可以使用 [UIEvents-Key] 中 Modifier Keys 表描述的 键修饰键名称来设置和检索。

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

4.2.3 MouseEvent 的全局状态

警告

本节需要修订。

4.2.3.1 用户代理级状态

UA 必须维护以下在整个用户代理中共享的值。

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

4.2.3.2 窗口级状态

UA 必须维护以下在 Window 中共享的值。

一个 最后 鼠标元素值(初始为 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()pospos 处最前面的 DOM 元素)

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

4.2.6 初始化 MouseEvent

警告

本节需要修订。

要使用 eventeventTypeeventTargetbubbles 以及 cancelable 初始化 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 处理原生 mouse down

警告

本节需要修订。

  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 处理原生 mouse up

警告

本节需要修订。

  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 处理原生 mouse click

警告

本节需要修订。

  1. native 为原生 mouse click

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

  2. target 为使用来自 native 的视口相对坐标进行 命中测试的结果
  3. 使用 nativetarget 发送 click 事件

4.2.15 发送 click 事件

警告

本节需要修订。

  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 处理原生 mouse double click

警告

本节需要修订。

  1. native 为原生 mouse double click

    对于会生成双击的鼠标点击,这应在 handle native mouse click 之后立即调用。

  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 处理原生 mouse move

警告

本节需要修订。

  1. native 为原生 mouse move
    议题 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. enterElementstargetDomPath 的副本,并移除所有 与 最后鼠标 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,则显示 UA 上下文菜单

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

4.3 Mouse Event 顺序

本规范中定义的某些鼠标事件 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 在 DOM 中是 A 的子元素:

三个堆叠元素彼此完全重叠的图形表示。底部元素标记为 A,顶部元素为 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 事件的滞后时间、程度、距离和数量, 将是实现、设备和平台特定的。这种容差可以帮助身体残障用户,例如手部不稳的用户, 在这些用户与指点设备交互时使用。

每个实现都会确定适当的 迟滞 容差,但通常在关联的 mousedownmouseup 事件的事件目标是同一元素,且中间没有 mouseoutmouseleave 事件介入时,SHOULD 触发 clickdblclick 事件;并且当 关联的 mousedownmouseup 事件目标 不同时,SHOULD 在最近的公共包含性 祖先上触发 clickdblclick 事件。

如果 mousedown 事件以 HTML 文档的 body 元素为目标,而对应的 mouseup 事件以 文档元素为目标, 则 click 事件将被分派 到 文档 元素,因为它是最近的公共包含性 祖先。

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

如果目标元素由于 mousedown 事件而被从 DOM 中移除,则不会为该元素 分派 mouseupclickdblclick 的事件,也不会分派任何默认 激活事件。然而,在初始目标元素被移除后,暴露给鼠标的元素仍会收到 mouseup 事件。 类似地,如果目标元素在 mouseup 事件的分派期间 被从 DOM 中移除,则 click 和后续事件将不会被分派。

4.4 Mouse Event 类型

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

4.4.1 auxclick

类型 auxclick
接口 PointerEvent
同步 / 异步 同步
冒泡
受信任目标 Element
可取消
Composed
默认动作 可变
上下文
(受信任事件)

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

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

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

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

  • 如果 目标具有关联的激活行为, 则 默认 动作 MUST 为执行该激活 行为。
示例 6:接收并处理中键的 auxclick
myLink.addEventListener("auxclick", function(e) {
  if (e.button === 1) {
    // 这会阻止默认行为,例如
    // 在链接上点击中键时打开新标签页。
    e.preventDefault();
    // 执行其他操作来处理中键点击,例如以适合该应用的方式
    // 负责在新标签页中打开链接或非链接按钮。
    // 其他动作,例如关闭标签条中的标签页,
    // 这些应在点击动作中完成的动作,也可以在这里完成。
  }
});

对于右键,auxclick 事件会在 任何 contextmenu 事件之后分派。注意,一些用户 代理会在上下文菜单显示期间吞掉所有输入 事件,因此在这些场景中 auxclick 可能无法供应用使用。 更多说明请参见 示例 7

示例 7:接收并处理右键的 auxclick
myDiv.addEventListener("contextmenu", function(e) {
  // 此调用确保不显示上下文菜单,
  // 以免干扰页面接收事件。
  e.preventDefault();
});
myDiv.addEventListener("auxclick", function(e) {
  if (e.button === 2) {
    // 执行其他操作来处理右键点击,例如打开
    // 应用内的自定义上下文菜单。
  }
});

4.4.2 click

类型 click
接口 PointerEvent
同步 / 异步 同步
冒泡
受信任目标 Element
可取消
Composed
默认动作 可变
上下文
(受信任事件)

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

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

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

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

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

为了获得最大的无障碍性,鼓励内容作者在为自定义 控件定义激活行为时使用 click 事件类型,而不是 mousedownmouseup 等更特定于设备的 其他指点设备事件类型。 尽管 click 事件类型源于指针 设备(例如鼠标),但后续的实现增强已将其扩展到该关联之外, 因而它可被视为用于元素激活的 与设备无关的事件类型。

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

4.4.3 contextmenu

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

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

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

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

4.4.4 dblclick

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

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

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

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

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

4.4.5 mousedown

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

许多实现使用 mousedown 事件来开始 各种依赖上下文的 默认动作。如果取消此事件,则可以阻止这些 默认动作。其中一些默认动作可能包括:开始与图像或链接的拖放 交互、开始文本选择,等等。 此外,一些实现提供鼠标驱动的平移 特性,该特性会在 mousedown 事件被分派时按下鼠标中键的情况下 激活。

4.4.6 mouseenter

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

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

4.4.7 mouseleave

类型 mouseleave
接口 MouseEvent
同步 / 异步 同步
冒泡
受信任目标 Element
可取消
Composed
默认动作
上下文
(受信任事件)
当指点设备从某元素及其所有后代元素的边界移出时, 用户 代理 MUST 分派此事件。当该元素或其某个后代不再位于 主指点设备下方时,用户 代理 MUST 也分派此事件。 此事件类型类似于 mouseout, 但不同之处在于它不冒泡,并且只有在指点设备已经离开该 元素的边界以及其所有子元素的边界之后,MUST NOT 才能 分派它。

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

4.4.8 mousemove

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

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

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

4.4.9 mouseout

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

另请参见 mouseover 事件类型。

4.4.10 mouseover

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

另请参见 mouseout 事件类型。

4.4.11 mouseup

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

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

5. Pointer Events 和接口

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-Y 平面上指向 X 值增大的方向(如果从正上方观察,则指向“3 点钟”方向), 并且这些值在顺时针方向上逐渐增大(π/2 为 “6 点钟”,π 为 “9 点钟”,3π/2 为 “12 点钟”)。 当换能器完全垂直于表面时(altitudeAngleπ/2),该值 MUST0。 对于不报告倾斜或角度的硬件和平台,该值 MUST0

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

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

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

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

有关 pointerType 如何使用的基本演示,请参见 示例 2。另请注意,开发者 应包含某种形式的默认处理,以覆盖用户代理可能已经实现了自己的自定义 pointerType 值的情况,以及 pointerType 只是空字符串的情况。
isPrimary

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

persistentDeviceId

指点设备的唯一标识符。如果硬件支持多个指针,则只有当这些指针在 会话期间可被唯一标识时,从指点设备生成的指针事件 MUST 才能获得 persistentDeviceId。如果指针可被唯一标识,则分配给该指点设备的 persistentDeviceId 将在该会话剩余期间保持不变。 persistentDeviceId0 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 实现该扩展的用户代理,在 clickauxclickcontextmenu 事件方面还有额外 要求。

5.1.1 按钮状态

5.1.1.1 组合按钮 交互

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

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

buttonbuttons 属性的修改仅适用于 pointer events。然而,对于 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 一个)会被视为主指针。例如,一个触摸接触点和 一个鼠标光标同时移动时,会产生两个都被视为主指针的指针。
某些设备、操作系统和用户代理可能会忽略多于一种类型的指针输入的并发使用, 以避免意外交互。例如,同时支持触摸和笔交互的设备可能会在笔正被 主动使用时忽略触摸输入,以允许用户在使用笔时将手放在触摸屏上 (此特性通常称为“掌部误触抑制”)。目前,作者无法抑制 此行为。
在某些情况下,用户代理可能会触发没有任何指针被标记为主指针的 指针事件。例如,当某个特定类型存在多个活动指针时,例如多点触控交互中, 主指针被移除(例如它离开屏幕),最终可能没有主指针。此外,在主指针 使用设备上同一类型的所有活动指针(包括那些以用户代理以外的应用为目标的指针) 来确定的平台上,如果第一个(主)指针位于用户代理之外,而其他(非主)指针 以用户代理内部为目标,则用户 代理 MAY 为其他指针触发 isPrimary 值为 false 的指针事件。
当前操作系统和用户代理通常没有多个鼠标输入的概念。 当存在多个鼠标设备时(例如笔记本电脑同时带有触控板和外接鼠标), 所有鼠标设备通常都会被当作一个设备处理——任何设备上的移动都会被转换为单个鼠标 指针的移动,并且不会区分不同鼠标设备上的按钮按下。因此,通常只会有一个 鼠标指针,并且该指针将是主指针。

5.1.3 使用 PointerEvent 接口触发事件

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

如果事件不是 gotpointercapturelostpointercaptureclickauxclickcontextmenu 事件,则为 此 PointerEvent 运行处理待处理的指针捕获步骤。

按如下方式确定 目标,事件会在该目标上触发:

targetDocument 为 target 的节点文档 [DOM]。

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

如果事件是 pointerdown,关联 设备是直接操纵设备,且目标是一个 Element, 则按照隐式指针捕获中所述,为此 pointerId设置指针捕获到目标元素。

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

向确定的目标触发该事件。

将确定的目标保存为给定指针的 previousTarget, 并将 needsOverEvent 标志重置为 false。 如果 previousTarget 在任何时候不再已连接 [DOM], 则按照与向 previousTarget 分派事件对应的事件路径, 将 previousTarget 更新为最近的仍然已连接 [DOM] 的父节点, 并将 needsOverEvent 标志设置为 true

使用指针捕获 目标覆盖作为目标,而不是普通的命中测试结果,可能会触发一些 边界事件,如 [UIEVENTS] 所定义。这与 指针离开其先前目标并进入这个新的捕获目标相同。当捕获被释放时, 也可能发生相同情形,因为指针正离开捕获目标并进入命中测试目标。
5.1.3.1 属性和默认动作

本规范中定义的事件类型的 bubblescancelable 属性以及默认动作 显示在下表中。每个事件类型的细节在Pointer Event 类型中提供。

事件类型 冒泡 可取消 默认动作
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。

许多用户代理在 MouseEvents 中暴露非标准属性 fromElementtoElement 以支持旧内容。我们鼓励这些用户 代理将 PointerEvents 中这些(继承的)属性的值设置为 null,以便将作者过渡到使用标准化的替代项 (targetrelatedTarget)。

MouseEvent relatedTarget 类似, relatedTarget 应初始化为指针刚离开其边界的元素(在 pointeroverpointerenter 事件的情况下),或初始化为指针正在进入其边界的元素(在 pointeroutpointerleave 的情况下)。对于其他指针事件, 此值将默认为 null。请注意,当某个元素接收指针捕获时,该指针随后的所有事件都被视为位于捕获 元素的边界内。

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

5.1.3.2 处理待处理的指针捕获

用户代理 MUST隐式释放指针 捕获时,以及在触发不是 gotpointercapturelostpointercapture 的 Pointer Events 时,运行以下步骤。

  1. 如果此指针的指针捕获 目标覆盖已设置,且不等于待处理的 指针捕获目标覆盖,则在指针捕获目标覆盖 节点上触发名为 lostpointercapture 的指针事件。
  2. 如果此指针的待处理的 指针捕获目标覆盖已设置,且不等于指针捕获目标覆盖, 则在待处理的 指针捕获目标覆盖上触发名为 gotpointercapture 的指针事件。
  3. 指针捕获目标覆盖设置为待处理的 指针捕获目标覆盖,如果已设置。否则,清除指针捕获目标覆盖

clickauxclickcontextmenu 事件章节所定义,即使在 lostpointercapture 事件已经 被分派之后, 对应的 clickauxclickcontextmenu 事件(如果有)仍会被分派到捕获目标。

5.1.3.3 抑制指针事件流

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

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

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

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

检测这些场景的方法不在本规范范围内。

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

5.1.4 由布局变化导致的边界事件

相对于屏幕表面移动,或其任一属性发生某些变化的指点设备,会触发 Pointer Event 类型中定义的各种事件。 对于静止的指点设备(既未相对于屏幕表面移动,也未经历任何属性变化), 如果布局变化影响了该指针的命中测试目标, 则用户代理 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] 规则对最终整数值进行舍入。

示例 8: 在 tiltX/tiltY 和 altitudeAngle/azimuthAngle 之间转换
/* 在 tiltX/tiltY 和 altitudeAngle/azimuthAngle 之间转换 */

function spherical2tilt(altitudeAngle, azimuthAngle) {
  const radToDeg = 180/Math.PI;

  let tiltXrad = 0;
  let tiltYrad = 0;

  if (altitudeAngle == 0) {
    // 笔位于 X-Y 平面内
    if (azimuthAngle == 0 || azimuthAngle == 2*Math.PI) {
      // 笔在正 X 轴上
      tiltXrad = Math.PI/2;
    }
    if (azimuthAngle == Math.PI/2) {
      // 笔在正 Y 轴上
      tiltYrad = Math.PI/2;
    }
    if (azimuthAngle == Math.PI) {
      // 笔在负 X 轴上
      tiltXrad = -Math.PI/2;
    }
    if (azimuthAngle == 3*Math.PI/2) {
      // 笔在负 Y 轴上
      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;

  // 计算方位角
  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) {
    // 没有足够信息来计算方位角
    azimuthAngle = 0;
  } else {
    // 非边界情况:tiltX 和 tiltY 都不等于 0 或 +-90
    const tanX = Math.tan(tiltXrad);
    const tanY = Math.tan(tiltYrad);

    azimuthAngle = Math.atan2(tanY, tanX);
    if (azimuthAngle < 0) {
      azimuthAngle += 2*Math.PI;
    }
  }

  // 计算高度角
  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 {
    // 非边界情况:tiltX 和 tiltY 都不等于 0 或 +-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

要使用 eventeventTypeeventTargetbubbles 以及 cancelable 初始化 PointerEvent, 运行以下步骤:

  1. 使用 eventeventTypeeventTargetbubbles 以及 cancelable 初始化 MouseEvent
  2. 将所有其他属性初始化为默认 PointerEvent 值。

5.2.2 创建 PointerEvent

要使用 eventTypeeventTargetbubbles 以及 cancelable 创建 PointerEvent,运行以下步骤:

  1. event 为使用 PointerEvent 创建 事件的结果
  2. 使用 eventeventTypeeventTargetbubbles 以及 cancelable 初始化 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 Event 类型

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

主指针的情况下,这些事件(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 事件

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

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

5.3.5 pointerrawupdate 事件

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

pointermove 相比,用户代理 SHOULD 尽可能快地,并以 JavaScript 能够处理这些事件的频率, 分派 pointerrawupdate 事件。

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 在捕获被释放后该指针的任何后续事件之前触发。 此事件会在其指针捕获被移除的元素上触发。该指针的所有后续事件,除clickauxclickcontextmenu 事件外,都遵循正常的命中测试 机制(不在本规范范围内)来确定事件目标。请参见释放指针捕获隐式释放指针 捕获处理待处理的指针 捕获章节。

5.3.12 clickauxclickcontextmenu 事件

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

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

5.3.12.1 事件属性

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

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

PointerEvent 中所述,CSSOM View Module 提议将各种坐标属性 (screenXscreenYpageXpageYclientXclientYxyoffsetXoffsetY)重新定义为 double,以允许小数坐标。 然而,当此更改只应用于 PointerEvent,而不应用于普通的 MouseEvent 时,事实证明,在 clickauxclickcontextmenu 的情况下, 会导致遗留代码的 Web 兼容性问题。因此,仅为 PointerEvent 实现了 CSSOM View Module 中所提议 更改的用户代理,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 事件,userEventpointerup; 对于 contextmenu 事件,userEventpointerdownpointerup 事件(二者取决于原生平台惯例)。

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

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

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

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

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

6. Wheel Events

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

用户的环境可能被配置为将垂直滚动 与沿 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;
	readonly attribute boolean momentum;
};
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
momentum
如果此事件是平台为了在用户结束物理滚动交互后 模拟滚动惯性(例如在快速滑动后从触控板抬起手指) 而合成的,则此属性必须为 true, 否则为 false。 此属性的未初始化值 MUSTfalse

6.1.2 WheelEventInit

WebIDLdictionary WheelEventInit : MouseEventInit {
	double deltaX = 0.0;
	double deltaY = 0.0;
	double deltaZ = 0.0;
	unsigned long deltaMode = 0;
	boolean momentum = false;
};
问题 3

当我们已经在上面定义了相应的 属性时,下面的定义似乎是冗余的。请参见 pointerevents/646

deltaX
deltaZ 属性。
deltaY
deltaZ 属性。
deltaZ
初始化 deltaZ 属性,该属性属于 WheelEvent 对象。此属性(以及 deltaXdeltaY 属性)的相对正值 由右手坐标系给出,其中 X、Y 和 Z 轴分别指向文档的 最右边缘、最下边缘和最远深度(远离用户)。 负的相对值则位于各自相反方向。
deltaMode
deltaMode 属性初始化到 WheelEvent 对象上,初始化为枚举值 0、 1 或 2,如果滚轮的旋转会导致滚动,则它们分别表示已滚动的像素量 (DOM_DELTA_PIXEL)、已滚动的行数 (DOM_DELTA_LINE)或已滚动的页数 (DOM_DELTA_PAGE)。
momentum
初始化 momentum 属性。

6.2 Wheel Event 类型

6.2.1 wheel

类型 wheel
接口 WheelEvent
同步 / 异步 异步
冒泡
受信任目标 Element
可取消 可变
Composed
默认动作 滚动(或缩放)文档
上下文
(受信任事件)
  • Event.target :当前滚轮事件事务的元素目标
  • UIEvent.viewWindow
  • UIEvent.detail0
  • 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 分派此事件。取决于平台和输入设备, 对角滚轮 deltas MAY 作为具有多个非零轴的单个 wheel 事件交付,或者作为每个非零轴的单独 wheel 事件交付。 wheel 事件类型的典型默认动作是 按所指示的量滚动(或在某些情况下缩放)文档。如果此事件被取消, 实现 MUST NOT 滚动或缩放文档 (或执行与此事件类型关联的任何其他 实现特定默认动作)。

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

6.2.2 wheel 事件的可取消性

在 wheel 事件上调用 preventDefault 可以阻止 或以其他方式中断滚动。为了获得最大的滚动性能,用户代理 可以不等待与滚动相关联的每个 wheel 事件被处理, 以查看它是否会被取消。在这种情况下,用户代理应生成 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 处于其活动按钮状态,否则它会静默失败。当提供的方法参数与任何 活动指针都不匹配时,抛出一个 "NotFoundError" DOMException

releasePointerCapture()

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

hasPointerCapture

指示调用此方法的元素是否对由参数 pointerId 标识的指针具有指针 捕获。具体而言,如果 pointerId待处理的指针捕获目标 覆盖被设置为调用此方法的元素,则返回 true,否则返回 false

此方法会在调用 setPointerCapture() 后立即返回 true,即使 该元素尚未收到 gotpointercapture 事件。因此, 它可用于从 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

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

10. 声明直接操纵行为

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

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

10.1 touch-action CSS 属性

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

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

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

某些用户代理会为涉及一系列独立离散手势、但全部作为 单个连续手势的一部分处理的行为实现复杂手势。 例如,考虑触摸屏上的“快速滑动滚动”手势:用户以快速手指移动开始平移文档, 从触摸屏抬起手指,文档继续以模拟惯性平移。当文档仍在移动时, 用户可以将手指放到触摸屏上并执行另一次“快速滑动”,以为平移提供进一步动量, 或抵消当前平移以减慢它、完全停止平移,或反转平移方向。 由于本规范不规范性地定义手势和行为如何实现,因此由用户代理决定 第二次触摸(在它被解释为第二次“快速滑动”或对当前平移的抵消之前) 是否触发指针事件。
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 值可用于自定义某些过度滚动行为。 例如,为实现简单的下拉刷新效果,当滚动位置为 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 将移除此延迟。请注意,确定轻触或双击手势的方法 不在本规范范围内。
示例 9:不允许 所有直接操纵行为
<div style="touch-action: none;">
    此元素接收所有否则会导致平移或缩放的直接操纵交互的指针事件。
</div>
示例 10:仅允许 水平平移
<div style="touch-action: pan-x;">
    当未在水平方向平移时,此元素接收指针事件。
</div>
示例 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>
示例 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"> 控件)。指针捕获可以设置在滑块拇指 元素上,从而允许用户来回滑动该控件,即使指针滑出了 拇指。

自定义音量滑块
7 一个自定义滑块控件的示例,该控件通过来回滑动拇指 元素来选择值。在拇指上发生 pointerdown 后,可以使用指针捕获 允许用户滑动拇指,即使指针偏离了它。

11.2 设置指针捕获

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

  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 监听器中尝试释放隐式指针捕获失败的情况,也是如此。

11.3 释放指针捕获

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

  1. 如果作为方法参数提供的 pointerId 与任何活动指针都不匹配,并且这些步骤 并非由于隐式释放指针 捕获而被调用,则抛出一个 "NotFoundError" DOMException
  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 被清除。

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

当指针锁定 [PointerLock] 成功应用到某元素上时,如果任何元素已被设置为捕获 或待捕获,用户代理 MUST 运行这些步骤,就像已经调用了 releasePointerCapture 方法一样。

12. 合并事件和预测事件

本规范没有定义用户代理应如何合并或 预测指针移动数据。它只指定用于访问此信息的 API。

12.1 合并事件

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

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

PointerEvent 具有关联的合并 事件列表(一个由零个或多个 PointerEvent 组成的列表)。 对于受信任的 pointermovepointerrawupdate 事件,该列表是 被合并到此事件中的所有 PointerEvent 的序列。 “父”受信任 pointermovepointerrawupdate 事件表示这些合并事件的 累积, 但可以有额外处理(例如与显示刷新率对齐)。 因此,这些事件的合并事件列表始终至少包含一个事件。 对于所有其他受信任事件类型,它是空列表。不受信任事件的 合并事件列表会初始化为传递给构造器的值。

由于受信任父事件是合并事件的摘要或聚合, 开发者应只需要处理父事件或所有合并事件中的一种, 而不是两者都处理。
当包含合并事件列表的受信任事件 从 JavaScript 被重新分派时,事件分派算法会将该事件的 isTrusted 位设置为 false,但合并 事件列表中的相同位保持其原始 true 值不变。

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

示例 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 值递增而实际发生的事件 以及用户代理分派的事件:

实际事件 已分派事件
pointer(pointerId=2) 坐标变化 pointerrawupdatepointerId=2),带一个 合并事件
pointer(pointerId=1) 坐标变化 pointerrawupdatepointerId=1),带一个 合并事件
pointer(pointerId=2) 坐标变化 pointerrawupdatepointerId=2),带一个 合并事件
pointer(pointerId=2) 坐标变化 pointerrawupdatepointerId=2),带一个 合并事件
pointer(pointerId=1) 坐标变化 pointerrawupdatepointerId=1),带一个 合并事件
pointer(pointerId=2) 坐标变化 pointerrawupdatepointerId=2),带一个 合并事件
pointer(pointerId=1)按钮 按下 pointermovepointerId=1),带两个 合并事件
pointermovepointerId=2),带四个 合并事件
pointerdownpointerId=1),带零个 合并事件
pointer(pointerId=2) 坐标变化 pointerrawupdatepointerId=2),带一个 合并事件
pointer(pointerId=2) 坐标变化 pointerrawupdatepointerId=2),带一个 合并事件
pointer(pointerId=1)按钮 释放 pointermovepointerId=2),带两个 合并事件
pointeruppointerId=1),带零个 合并事件

12.2 预测事件

某些用户代理内置了算法,这些算法在一系列已确认的指针移动之后, 可以(基于当前手势的前置事件以及移动的速度/轨迹) 预测未来指针移动的位置可能在哪里。应用可以通过 getPredictedEvents 方法使用此信息, 推测性地“提前绘制”到预测位置, 以降低感知延迟,然后在接收到实际点后丢弃这些预测点。

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

PointerEvent 具有关联的预测 事件列表(一个由零个或多个 PointerEvent 组成的列表)。对于受信任的 pointermove 事件,它是用户代理预测将在未来跟随该事件的 PointerEvent 的序列。 对于所有其他受信任事件类型,它是空列表。 不受信任事件的预测事件列表会初始化 为传递给构造器的值。

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

当包含预测事件列表的受信任事件 从 JavaScript 被重新分派时,事件分派算法会将该事件的 isTrusted 位设置为 false,但预测 事件列表中的相同位保持其原始 true 值不变。

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

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

请注意,作者只应在下一个指针事件被 分派之前,将预测事件视为有效预测。取决于用户代理预测事件到未来的距离, 常规指针事件可能会早于一个或多个预测事件的时间戳被分派。

示例 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 特性。 鼓励用户代理支持此特性,以便与现有旧式内容获得最佳兼容性。

从高层来看,兼容性鼠标事件旨在与其各自的指针事件“交错”。 然而,此特定顺序不是强制性的,实现兼容性鼠标事件的用户代理 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],分派 mouseovermouseoutmouseentermouseleave 事件,表示鼠标从当前旧式鼠标指针的有效 位置移动到 T。将当前旧式鼠标指针的有效 位置T 的未设置值视为窗口外的鼠标位置。
  4. 旧式鼠标指针的有效 位置设置为 T

旧式 鼠标指针的有效位置模拟了这样一个事实:我们并不总是能够从指针过渡事件 (pointeroverpointeroutpointerenterpointerleave)直接映射到对应的旧式鼠标过渡事件 (mouseovermouseoutmouseentermouseleave)。下面的 动画展示了这样一种情况:用户代理需要分派比指针过渡事件更多的旧式鼠标过渡事件, 才能使用单个旧式鼠标输入协调两个主指针。

10 同时存在的鼠标指针(白色光标)和触摸指针(白色“手形”光标) 导致单个旧式鼠标输入 (橙色光标)在两个指针之间移动。

在此动画中,请注意鼠标点击和触摸轻触之间的时间段。Button 1 没有收到 pointerout 事件(因为“真实”鼠标指针在 此时间段内没有离开按钮矩形),但当旧式 鼠标指针的有效位置在触摸轻触时移动到 Button 2 时,Button 1 会收到 mouseout 事件。类似地,在触摸轻触和鼠标离开 Button 1 之前的这段时间内, Button 1 也因相同原因没有收到 pointerover 事件,但当旧式鼠标指针的有效位置 移回 Button 1 内部时,Button 1 会收到 mouseover 事件。

13.2 支持悬停的设备的映射

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

  1. 如果要分派的指针事件的 isPrimary 属性为 false, 则分派该指针事件并终止这些步骤。
  2. 如果要分派的指针事件是 pointerdownpointeruppointermove 事件,或者是在 window 上的 pointerleave 事件, 则按照跟踪旧式鼠标指针的有效位置中所述,分派兼容性鼠标过渡事件。
  3. 分派指针事件。
  4. 如果已分派的指针事件是 pointerdown,且事件的已取消标志已设置,则为此 pointerType 设置 PREVENT MOUSE EVENT 标志。
  5. 如果未为此 pointerType 设置 PREVENT 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,且事件的已取消标志已设置,则为此 pointerType 设置 PREVENT MOUSE EVENT 标志。
  6. 如果未为此 pointerType 设置 PREVENT MOUSE EVENT 标志,并且已分派的指针事件是:
  7. 如果已分派的指针事件是 pointeruppointercancel,则清除此 pointerTypePREVENT MOUSE EVENT 标志。

如果用户代理同时支持 Touch Events(如 [TOUCH-EVENTS] 中定义) 和 Pointer 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 事件的已取消标志, 则事件序列将为:

  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 访问这些传感器。

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

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

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

除这些考量外,工作组认为本规范:

15. 术语表

本节是非规范性的。

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

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

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

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

16. 旧式事件初始化器

本节是规范性的。 以下特性已过时,并且只应由需要 与旧式软件兼容的用户代理实现。 另请参见 [UIEvents] 中的旧式事件初始化器

16.1 接口 MouseEvent 的初始化器

WebIDLpartial interface MouseEvent {
	// 在本规范中已弃用
	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;
	readonly attribute boolean momentum;
};

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

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 Overflow Module Level 3. Elika Etemad; Florian Rivoal. W3C. 2025 年 10 月 7 日。W3C 工作草案。URL:https://www.w3.org/TR/css-overflow-3/
[css-position]
CSS Positioned Layout Module Level 3. Elika Etemad; Tab Atkins Jr. W3C. 2025 年 10 月 7 日。W3C 工作草案。URL:https://www.w3.org/TR/css-position-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/
[HTML401]
HTML 4.01 Specification. Dave Raggett; Arnaud Le Hors; Ian Jacobs. W3C. 2018 年 3 月 27 日。W3C 推荐标准。URL:https://www.w3.org/TR/html401/
[infra]
Infra Standard. Anne van Kesteren; Domenic Denicola. WHATWG. 现行标准。URL:https://infra.spec.whatwg.org/
[PointerEvents]
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/
[UIEvents-Key]
UI Events KeyboardEvent key Values. 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 年 5 月 22 日。W3C 工作草案。URL:https://www.w3.org/TR/webdriver-bidi/
[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/
[PointerEvents3]
Pointer Events. Patrick Lauke; Robert Flack. W3C. 2026 年 5 月 22 日。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. 2024 年 12 月 12 日。W3C 推荐标准。URL:https://www.w3.org/TR/WCAG22/