指针事件 (Pointer Events)

Level 4

W3C 工作草案

关于此文档的更多细节
此版本
https://w3org.cn/TR/2026/WD-pointerevents4-20260225/
最新发布版本
https://w3org.cn/TR/pointerevents4/
最新编辑草案
https://w3c.github.io/pointerevents/
历史
https://w3org.cn/standards/history/pointerevents4/
提交历史
测试套件
https://wpt.fyi/pointerevents/
最新推荐标准
https://w3org.cn/TR/pointerevents2
编辑
Patrick H. Lauke (TetraLogical)
Robert Flack (Google)
前任编辑
Matt Brubeck (Mozilla)
Rick Byers (Google)
Navid Zolghadr (Google)
反馈
GitHub w3c/pointerevents (pull requests, 创建新 issue, 打开的 issues)
public-pointer-events@w3.org,邮件主题请以 [pointerevents4] … 消息主题 … 开头 (存档)
浏览器支持
caniuse.com

摘要

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

本规范还定义了鼠标和滚轮事件,以及将鼠标事件映射到其他指针设备类型的方法。

本文档状态

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

本规范是对 [PointerEvents3] 规范的更新。它还包含了之前位于 [UIEVENTS] 规范中的鼠标和滚轮事件。

本修订版包含的新特性:

本文档由 指针事件工作组 (Pointer Events Working Group) 作为工作草案发布,采用 推荐标准流程 (Recommendation track)

发布为工作草案并不意味着 W3C 及其成员的认可。

这是一份草案文件,可能随时被其他文件更新、替换或废弃。将其作为进展中的工作以外的引用是不恰当的。

本文档由在 W3C 专利政策下运作的组织编写。W3C 维护一份与该组交付成果相关的专利披露公开列表;该页面还包括披露专利的说明。任何知悉某项专利并认为其包含 必要权利要求 (Essential Claim(s)) 的个人,必须按照 W3C 专利政策第 6 节的要求披露该信息。

本文件受 2025 年 8 月 18 日 W3C 流程文档约束。

1. 简介

本节是非规范性的。

目前,大多数 [HTML] 内容均与鼠标输入一起使用或为此设计。那些以自定义方式处理输入的通常会按照 [UIEVENTS] 的鼠标事件进行编码。然而,当今较新的计算设备集成了其他输入形式,包括触摸屏和手写笔输入。已经提出了分别处理这些输入形式的事件类型。但是,这种方法在添加新输入类型支持时,通常会导致逻辑重复和事件处理开销。当内容仅针对一种设备类型编写时,这往往会产生兼容性问题。此外,为了与现有的基于鼠标的内容兼容,大多数 用户代理 (user agents) 会针对所有输入类型触发鼠标事件。这使得鼠标事件代表的是真实的鼠标设备,还是为了兼容性由其他输入类型产生的,变得难以区分,从而难以同时针对两种设备类型进行编码。

为了降低针对多种输入类型进行编码的成本,并缓解上述鼠标事件的歧义,本规范定义了一种更抽象的输入形式,称为 指针 (pointer)。指针可以是鼠标光标、触控笔、触摸(包括多点触控)或其他指针输入设备在屏幕上产生的任何接触点。这种模型使得编写在用户使用任何硬件时都能良好运行的网站和应用程序变得更加容易。对于需要设备特定处理的场景,本规范还定义了用于检查产生事件的设备类型的属性。主要目标是提供一组统一的事件和接口,以便更轻松地编写跨设备的指针输入,同时仅在必要时才允许进行特定于设备的处理以提升体验。

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

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

首先,鼓励作者通过响应高级事件(如 focusblurclick)来为所有输入形式提供等效功能。然而,当使用低级事件(如指针事件)时,鼓励作者确保支持所有类型的输入。对于键盘和类键盘接口,这可能需要添加显式的键盘事件处理。有关详细信息,请参阅 [WCAG22] 中的 键盘可访问性 (Keyboard Accessible)

Pointer input combines various input sources such as mouse, pen, and touch
1 指针是硬件无关的输入设备表示,可以针对屏幕上的特定坐标(或一组坐标)。

用于处理通用指针输入的事件看起来与鼠标事件非常相似:pointerdownpointermovepointeruppointeroverpointerout 等。这促进了内容从鼠标事件到指针事件的轻松迁移。指针事件除了提供鼠标事件中常见的所有属性(包括客户端坐标、目标元素、按钮状态)外,还提供了用于其他输入形式(如压力、接触几何形状、倾斜)的新属性。作者可以轻松地编写指针事件逻辑,在合理的情况下共享不同输入类型之间的逻辑,并仅在必要时自定义特定类型的输入以获得最佳体验。

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

本规范未提供关于支持触摸事件(如 [TOUCH-EVENTS] 中定义的)和指针事件的用户代理的预期行为的任何建议。有关这两个规范之间关系的更多信息,请参阅 触摸事件社区组 (Touch Events Community Group)

2. 一致性

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

本文档中的关键词 MAY(可能)、MUST(必须)、MUST NOT(不得)、OPTIONAL(可选)和 SHOULD(应该)仅在全大写显示时,按照 [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

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

此属性的 未初始化值 (un-initialized value) MUST0

screenY

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

此属性的 未初始化值 (un-initialized value) MUST0

clientX

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

此属性的 未初始化值 (un-initialized value) MUST0

clientY

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

此属性的 未初始化值 (un-initialized value) MUST0

layerX

相对于最近的 祖先 元素的水平偏移,该祖先元素必须是 堆叠上下文 (stacking context)、已 定位 (positioned),或在 绘制堆叠上下文 时在定位阶段进行绘制。

此属性的 未初始化值 (un-initialized value) MUST0

layerY

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

此属性的 未初始化值 (un-initialized value) 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(前进)按钮。

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

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

某些与 mousedownmouseup 等事件相关的 默认行为 (default actions) 取决于所使用的特定鼠标按钮。

此属性的 未初始化值 (un-initialized value) MUST0

按钮

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

尽管命名相似,buttons 属性和 button 属性的值截然不同。button 的值在 mousedown / mouseup 事件处理程序期间被认为是有效的,而 buttons 属性反映了任何受信任的 MouseEvent 对象在被分发时鼠标按钮的状态(因为它能表示“当前无激活按钮”状态,即 0)。

buttons 属性的值 MUST 如下:

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

一些指针设备提供或模拟更多按钮。为了表示此类按钮,对于每个连续的按钮,该值 MUST 加倍(按照二进制系列 8, 16, 32, ...)。

因为任何一组按钮值的总和都是唯一的数字,所以内容作者可以使用按位运算来确定当前按下了多少个按钮以及是哪些按钮,即使设备上有任意数量的鼠标按钮。例如,值 3 表示当前同时按下了左键和右键,而值 5 表示当前同时按下了左键和中键。

某些与 mousedownmouseup 等事件相关的 默认行为 取决于所使用的特定鼠标按钮。

此属性的 未初始化值 (un-initialized value) 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

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

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

screenY

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

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

clientX

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

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

clientY

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

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

button

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

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

按钮

MouseEvent 对象的 buttons 属性初始化为表示被视为激活的一个 或多个 鼠标按钮的数字。

buttons 属性是一个位字段。如果掩码值 1 在应用于位字段值时为真,则主鼠标按钮已按下。如果掩码值 2 在应用于位字段值时为真,则右侧鼠标按钮已按下。如果掩码值 4 在应用于位字段值时为真,则辅助/中间按钮已按下。

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

{ buttons: 2 | 4 }

或者

{ buttons: 6 }
relatedTarget

relatedTarget 应初始化为鼠标指针刚刚离开其边界的元素(在 mouseovermouseenter 事件的情况下),或者是鼠标指针正进入的元素(在 mouseoutmouseleavefocusout 事件的情况下)。对于其他事件,此值不需要分配(默认为 null)。

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

4.2 MouseEvent 算法

4.2.1 原生操作系统要求

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

  • 鼠标移动时的事件(由 处理原生鼠标移动 处理)
  • 按下鼠标按钮时的事件(由 处理原生鼠标按下 处理)
  • 释放鼠标按钮时的事件(由 处理原生鼠标释放 处理)
  • 识别何时应将鼠标按钮按下解释为“点击”的方法(由 处理原生鼠标点击 处理)
  • 例如,作为标志或作为单独的事件
  • 如果触发了单独的“点击”事件,则原生操作系统会在相应的“鼠标释放”事件之后立即触发它,且期间没有其他鼠标相关事件
  • 识别何时鼠标点击是“双击”的方法(由 处理原生鼠标双击 处理)

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

    • 相对于原生操作系统桌面的 x,y 鼠标坐标
    • 相对于 UA 窗口视口的 x,y 鼠标坐标
    • 当前持有的键盘修饰符
  • 4.2.2 构建鼠标事件

    Warning

    本节需要修订。

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

    为了构建 MouseEvent 或使用以下算法从这些对象派生的对象,所有 MouseEvent 及派生对象都具有 内部按键修饰符状态 (internal key modifier state),可以使用 [UIEvents-Key] 中 修饰键表格 (Modifier Keys table) 中描述的 按键修饰符名称 (key modifier names) 进行设置和检索。

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

    4.2.3 MouseEvent 的全局状态

    Warning

    本节需要修订。

    4.2.3.1 用户代理级状态

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

    一个用于追踪鼠标按钮当前状态的 鼠标按钮位掩码 (mouse button bitmask)

    4.2.3.2 窗口级状态

    UA 必须维护以下在窗口中共享的值:

    一个 上一个鼠标元素 (last mouse element) 值(初始未定义),用于追踪我们发送 MouseEvent 到的上一个 Element

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

    4.2.4 MouseEvent 的内部状态

    Warning

    本节需要修订。

    MouseEvent 具有以下用于追踪各种修饰键状态的内部标志:Shift 标志Control 标志Alt 标志AltGraph 标志Meta 标志。如果在鼠标事件发生时按下了相应的修饰键,则设置这些标志。

    4.2.5 命中测试 (hit test)

    Warning

    本节需要修订。

    1. pos 为相对于视口的 x,y 坐标。
    2. 返回 [CSSOM-View] 的 elementFromPoint() 作用于 pos 的结果(pos 处最前面的 DOM 元素)。

      为了考虑 惰性 (inert)禁用 (disabled) 元素,这应调用 elementsFromPoint() 并拒绝无效元素。

    4.2.6 初始化 MouseEvent

    Warning

    本节需要修订。

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

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

      我们应该为 PointerLock 提供一个钩子,而不是在这里硬编码它。

    4.2.7 设置鼠标事件修饰符

    Warning

    本节需要修订。

    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

    Warning

    本节需要修订。

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

    4.2.9 创建不可取消的 MouseEvent

    Warning

    本节需要修订。

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

    4.2.10 计算 MouseEvent 的 button 属性

    Warning

    本节需要修订。

    这将返回一个适合存储在 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 属性

    Warning

    本节需要修订。

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

      待办 (TODO)。

    3. 如果 event.type 是 [ mousedown, mouseup ] 之一,则:
    4. mbutton 为从 native 获取的识别哪个鼠标按钮被按下的 ID。
    5. event.button 设置为 计算 MouseEvent button 属性 的结果(传入 mbutton)。

    4.2.12 处理原生鼠标按下

    Warning

    本节需要修订。

    1. native 为原生 mousedown。
    2. mbutton 为从 native 获取的识别哪个鼠标按钮被按下的 ID。
    3. 按照以下方式更新 鼠标按钮位掩码
    4. 如果 mbutton 是主鼠标按钮,则设置 0x01 位。
    5. 如果 mbutton 是次要鼠标按钮,则设置 0x02 位。
    6. 如果 mbutton 是辅助(中)鼠标按钮,则设置 0x04 位。

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

  • target 为使用来自 native 的视口相关坐标执行 命中测试 的结果。
  • event 为使用 "mousedown" 和 target 创建可取消的 MouseEvent 的结果。
  • 从原生事件设置 MouseEvent 属性,传入 native
  • 可能发送 pointerdown 事件,传入 event
  • result 为在 target分发 (dispatch) event 的结果。
  • 如果 result 为 true 且 target 是一个 可聚焦区域 (focusable area) 且是 点击可聚焦 (click focusable) 的,则:
  • target 上运行 聚焦步骤 (focusing steps)
  • 如果 mbutton 是次要鼠标按钮,则:
  • 可能显示上下文菜单,传入 nativetarget
  • 4.2.13 处理原生鼠标释放

    Warning

    本节需要修订。

    1. native 为原生 mouseup。

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

    2. mbutton 为从 native 获取的识别哪个鼠标按钮被按下的 ID。
    3. 按照以下方式更新 鼠标按钮位掩码
    4. 如果 mbutton 是主鼠标按钮,则清除 0x01 位。
    5. 如果 mbutton 是次要鼠标按钮,则清除 0x02 位。
    6. 如果 mbutton 是辅助(中)鼠标按钮,则清除 0x04 位。
  • target 为使用来自 native 的视口相关坐标执行 命中测试 的结果。
  • event 为使用 "mouseup" 和 target 创建可取消的 MouseEvent 的结果。
  • 从原生事件设置 MouseEvent 属性,传入 native
  • 可能发送 pointerup 事件,传入 event
  • target分发 event
  • 4.2.14 处理原生鼠标点击

    Warning

    本节需要修订。

    1. native 为原生鼠标点击。

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

    2. target 为使用来自 native 的视口相关坐标执行 命中测试 的结果。
    3. 发送点击事件,传入 nativetarget

    4.2.15 发送点击事件

    Warning

    本节需要修订。

    1. native 为原生 mousedown。
    2. target 为事件的 EventTarget
    3. mbutton 为 1(默认主鼠标按钮)。
    4. 如果 native 有效,则:
    5. mbutton 为从 native 获取的识别哪个鼠标按钮被按下的 ID。
  • 如果 mbutton 是主鼠标按钮,则将 eventType 设置为 "click",否则设置为 "auxclick"。
  • event 为使用 eventTypetarget 创建 PointerEvent 的结果。
  • 如果 native 有效,则:
  • 从原生事件设置 MouseEvent 属性,传入 eventnative
  • 如果 event.screenX 不是整数值,则四舍五入。
  • 如果 event.screenY 不是整数值,则四舍五入。
  • target分发 event

    有关使用指针事件和舍入坐标的浏览器的信息,请参阅 pointerevents/100

    编辑注释

    任何“默认行为”在分发期间通过触发目标的 激活行为 (activation behavior) 算法来处理。因此,无需在此处处理。但是,需要验证现有规范是否处理了已禁用/css-pointer-events/惰性/...的情况。

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

    要处理键盘触发的点击,请以 native = null 和 target = 当前聚焦元素调用此算法。

  • 4.2.16 处理原生鼠标双击

    Warning

    本节需要修订。

    1. native 为原生鼠标双击。

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

    2. mbutton 为从 native 获取的识别哪个鼠标按钮被按下的 ID。
    3. 如果 mbutton 不是主鼠标按钮,则返回。
    4. target 为使用来自 native 的视口相关坐标执行 命中测试 的结果。
    5. event 为使用 "dblclick" 和 target 创建 PointerEvent 的结果。
    6. 从原生事件设置 MouseEvent 属性,传入 eventnative
    7. 如果 event.screenX 不是整数值,则四舍五入。
    8. 如果 event.screenY 不是整数值,则四舍五入。
    9. target分发 event

    4.2.17 处理原生鼠标移动

    Warning

    本节需要修订。

    1. native 为原生鼠标移动。
      Issue 2

      此算法对指针事件的分发做出了假设,因为它们目前尚未明确指定。一旦 pointerevents/285 得到解决,这可能需要更新。

    2. target 为使用来自 native 的视口相关坐标执行 命中测试 的结果。
    3. targetDomPathtarget包含祖先 (inclusive ancestors)
    4. 生成离开当前元素的事件。
    5. 如果定义了 上一个鼠标元素 且不等于 target,则:
    6. mouseout 为使用 "mouseout" 和 上一个鼠标元素 创建可取消的 MouseEvent 的结果。
      编辑注释

      待办:从 native 设置 mouseout 属性。+CSSOM 属性。

  • 可能发送 pointerout 事件,传入 mouseout
  • 分发 mouseouttarget

    验证取消时的行为(似乎没有影响)。

  • leaveElements上一个鼠标 DOM 路径 的副本,并移除了所有 targetDomPath 中的公共元素。
  • 对于 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. 可能发送 pointerleave 事件,传入 mouseleave
    4. result 为在 element分发 mouseleave 的结果。
  • 生成进入新元素的事件。
  • 如果 target 不是 上一个鼠标元素,则:
  • mouseover 为使用 "mouseover" 和 target 创建可取消的 MouseEvent 的结果。
    编辑注释

    待办:从 native 设置 mouseout 属性。+CSSOM 属性。

  • 可能发送 pointerover 事件,传入 mouseover
  • 分发 mouseouttarget

    需要验证取消时的行为(似乎没有影响)。

  • enterElementstargetDomPath 的副本,并移除了所有 上一个鼠标 DOM 路径 中的公共元素。
  • 对于 enterElements 中的每个 element,执行:

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

    1. mouseenter 为使用 "mouseenter" 和 element 创建不可取消的 MouseEvent 的结果。
    2. 设置 mouseenter.Event.composed = false。

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

    3. 可能发送 pointerenter 事件,传入 mouseenter

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

    4. result 为在 element分发 mouseenter 的结果。
  • 上一个鼠标元素 设置为 target
  • 上一个鼠标 DOM 路径 设置为 targetDomPath
  • mousemove 为使用 "mousemove" 和 element 创建可取消的 MouseEvent 的结果。
  • 设置 mousemove 的 PointerLock 属性。
  • 可能发送 pointermove 事件,传入 mousemove
  • 分发 mousemoveelement
  • 4.2.18 可能显示上下文菜单

    Warning

    本节需要修订。

    1. native 为原生 mousedown 或指针事件。
    2. target 为事件的 EventTarget
    3. menuevent 为使用 "contextmenu" 和 target 创建 PointerEvent 的结果。
    4. 如果 native 有效,则:
    5. 从原生事件设置 MouseEvent 属性,传入 native
  • result 为在 target分发 menuevent 的结果。
  • 如果 result 为 true,则显示 UA 上下文菜单。
  • 要处理键盘触发的上下文菜单,请以 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 的三个元素在网页上具有相同的尺寸和绝对位置。在 DOM 中,元素 C 是 B 的子元素,B 是 A 的子元素:

    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 可选,多个事件,存在一些限制
    3 mouseup
    4 click
    5 mousemove 可选,多个事件,存在一些限制
    6 mousedown
    7 mousemove 可选,多个事件,存在一些限制
    8 mouseup
    9 click
    10 dblclick

    mousedownmouseup 事件之间允许的 mousemove 事件的滞后时间、程度、距离和数量,在触发 clickdblclick 事件时,将取决于具体实现、设备和平台。这种容差可以帮助那些手部不稳等有身体残障的用户在使用定点设备时进行交互。

    每个实现都将确定适当的 滞后(hysteresis) 容差,但通常情况下 应该 在关联的 mousedownmouseup 事件的目标元素相同时,且中间没有 mouseoutmouseleave 事件介入时,触发 clickdblclick 事件;当关联的 mousedownmouseup 事件的目标元素不同时,应该 在最近的共同包含祖先元素上触发 clickdblclick 事件。

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

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

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

    4.4 鼠标事件类型

    鼠标事件类型如下所列。对于嵌套元素,鼠标事件类型始终以最深层嵌套的元素为目标。目标元素的祖先 可以 使用冒泡来获取在其后代元素内发生的鼠标事件的通知。

    4.4.1 auxclick

    类型auxclick
    InterfacePointerEvent
    同步 / 异步同步
    冒泡
    受信任的目标元素
    可取消
    可组合(Composed)
    默认操作不同
    上下文
    (受信任事件)

    auxclick 事件类型 必须 在用户按下并释放非主要指针按钮,或以模拟此类操作的方式激活指针时,在由指针指示的 最顶层事件目标 上分派。鼠标按钮的触发方式取决于指针设备和环境配置,例如,它 可能 取决于屏幕位置或定点设备按钮按下与释放之间的延迟。

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

    auxclick 事件之前 可能 会在同一元素上伴随 mousedownmouseup 事件,而不考虑其他节点类型(例如文本节点)之间的变化。根据环境配置,如果 mouseovermousemovemouseout 中的一个或多个事件类型在定点设备按钮按下与释放之间发生,则 可能 会分派 auxclick 事件。

    auxclick 事件类型的 默认操作 根据事件的 目标 以及 buttonbuttons 属性的值而有所不同。auxclick 事件类型的典型 默认操作 如下:

    • 如果 目标 具有关联的激活行为,则 默认操作 必须 是执行该激活行为。
    示例 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 可能无法被应用程序获取。请参阅 示例 7 以获取更多说明。

    示例 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
    InterfacePointerEvent
    同步 / 异步同步
    冒泡
    受信任的目标元素
    可取消
    可组合(Composed)
    默认操作不同
    上下文
    (受信任事件)

    click 事件类型 必须 在用户按下并释放主要指针按钮,或以模拟此类操作的方式激活指针时,在由指针指示的 最顶层事件目标 上分派。鼠标按钮的触发方式取决于指针设备和环境配置,例如,它 可能 取决于屏幕位置或定点设备按钮按下与释放之间的延迟。

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

    click 事件之前 可能 会在同一元素上伴随 mousedownmouseup 事件,而不考虑其他节点类型(例如文本节点)之间的变化。根据环境配置,如果 mouseovermousemovemouseout 中的一个或多个事件类型在定点设备按钮按下与释放之间发生,则 可能 会分派 click 事件。click 事件之后 可能 还会紧随 dblclick 事件。

    如果用户在一个 <p> 元素的文本节点子项上按下鼠标(该元素已使用较大的 line-height 设置样式),稍微移动鼠标使其不再覆盖包含文本的区域,但仍在该 <p> 元素的包含块内(即指针位于同一文本块的行之间,但并未直接覆盖文本节点本身),然后随后释放鼠标,这很可能会触发 click 事件(如果它落在 click 的正常时间 滞后(hysteresis) 范围内),因为用户仍保留在同一元素的作用域内。请注意,用户代理生成的鼠标事件不会在文本节点上分派。

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

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

    click 事件类型的 默认操作 根据事件的 目标 以及 buttonbuttons 属性的值而有所不同。click 事件类型的典型 默认操作 如下:

    • 如果 目标 具有关联的激活行为,则 默认操作 必须 是执行该激活行为。
    • 如果 目标 可聚焦,则 默认操作 必须 是给予该元素文档焦点。

    4.4.3 contextmenu

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

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

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

    根据平台的不同,contextmenu 事件可能在 mouseup 事件之前或之后分派。

    4.4.4 dblclick

    类型 dblclick
    Interface MouseEvent
    同步 / 异步同步
    冒泡
    受信任的目标元素
    可取消
    可组合(Composed)
    默认操作
    上下文
    (受信任事件)

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

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

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

    click 事件类型一样,dblclick 事件类型的 默认操作 根据事件的 目标 以及 buttonbuttons 属性的值而有所不同。dblclick 事件类型的典型 默认操作click 事件类型的操作匹配。

    4.4.5 mousedown

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

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

    4.4.6 mouseenter

    类型mouseenter
    InterfaceMouseEvent
    同步 / 异步同步
    冒泡No
    受信任的目标元素
    可取消No
    可组合(Composed)No
    默认操作
    上下文
    (受信任事件)
    当定点设备移动到元素或其后代元素之一的边界内时,用户代理 必须 分派此事件。当元素或其后代之一移动到主要定点设备下方时,用户代理 必须 也分派此事件。此事件类型类似于 mouseover,但区别在于它不会冒泡,并且当指针设备从一个元素移动到其后代元素的边界时,不得 分派此事件。

    此事件类型与 CSS :hover 伪类 [CSS2] 之间有相似之处。另请参阅 mouseleave 事件类型。

    4.4.7 mouseleave

    类型mouseleave
    InterfaceMouseEvent
    同步 / 异步同步
    冒泡No
    受信任的目标元素
    可取消No
    可组合(Composed)No
    默认操作
    上下文
    (受信任事件)
    当定点设备移动到元素及其所有后代元素的边界之外时,用户代理 必须 分派此事件。当元素或其后代之一移动到不再位于主要定点设备下方时,用户代理 必须 也分派此事件。此事件类型类似于 mouseout,但区别在于它不会冒泡,并且直到定点设备离开该元素及其所有子元素的边界之前,不得 分派此事件。

    此事件类型与 CSS :hover 伪类 [CSS2] 之间有相似之处。另请参阅 mouseenter 事件类型。

    4.4.8 mousemove

    类型mousemove
    InterfaceMouseEvent
    同步 / 异步同步
    冒泡
    受信任的目标元素
    可取消
    可组合(Composed)
    默认操作
    上下文
    (受信任事件)
    当定点设备在元素上方移动时,用户代理 必须 分派此事件。定点设备移动时的事件频率取决于具体实现、设备和平台,但对于持续的指针设备移动,应该 触发多个连续的 mousemove 事件,而不是为每次鼠标移动触发单个事件。鼓励各实现确定最佳频率,以平衡响应性和性能。

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

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

    4.4.9 mouseout

    类型mouseout
    InterfaceMouseEvent
    同步 / 异步同步
    冒泡
    受信任的目标元素
    可取消
    可组合(Composed)
    默认操作
    上下文
    (受信任事件)
    当定点设备从元素边界移出,或元素移动到不再位于主定点设备下方时,用户代理 必须分发此事件。此事件类型类似于 mouseleave,但不同之处在于它会冒泡,且当定点设备从一个元素移动到其子元素边界上时,它 必须被分发。

    另请参阅 mouseover 事件类型。

    4.4.10 mouseover

    类型mouseover
    InterfaceMouseEvent
    同步 / 异步同步
    冒泡
    受信任的目标元素
    可取消
    可组合(Composed)
    默认操作
    上下文
    (受信任事件)
    当定点设备移动到元素边界上,或元素移动到主定点设备下方时,用户代理 必须分发此事件。此事件类型类似于 mouseenter,但不同之处在于它会冒泡,且当定点设备移动到其祖先元素为同一 事件监听器 实例的 目标 的元素边界上时,它 必须被分发。

    另请参阅 mouseout 事件类型。

    4.4.11 mouseup

    类型mouseup
    InterfaceMouseEvent
    同步 / 异步同步
    冒泡
    受信任的目标元素
    可取消
    可组合(Composed)
    默认操作
    上下文
    (受信任事件)
    当定点设备按钮在元素上方释放时,用户代理 必须分发此事件。

    在某些实现环境(如浏览器)中,即使定点设备已经离开了用户代理的边界,也可以分发 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

    导致事件的指针的唯一标识符。用户代理 可以为主要鼠标指针保留 01 的通用 pointerId 值。-1pointerId必须被保留,用于指示由非定点设备生成的事件。对于任何其他指针,用户代理可以自由实施不同的策略和方法来分配 pointerId 值。然而,顶级浏览上下文(由 [HTML] 定义)中的所有 活动指针 必须是唯一的,且该标识符 绝不能受到任何其他顶级浏览上下文的影响(即,一个顶级浏览上下文不能假设当指针移动到浏览上下文之外并进入另一个顶级浏览上下文时,其 pointerId 是相同的)。

    用户代理 可以回收之前已停用的 pointerId 值,或者对于特定的定点设备总是重用相同的 pointerId(例如,在多用户协作应用中唯一标识来自特定用户的特定笔/手写笔输入)。但在后一种情况下,为了最大限度地减少跨页面或域进行指纹识别和跟踪的机会,pointerId 必须仅在页面/会话的生命周期内显式地与该特定定点设备关联,并且在下次新会话中再次使用该特定定点设备时,必须选择一个新的随机化 pointerId

    pointerId 选择算法是特定于实现的。作者不能假设这些值传达了除唯一标识指针之外的任何特定含义。例如,用户代理可以简单地按顺序为任何活动的指针分配从 0 开始的数字——但不能保证这些值是单调递增的。由于为特定定点设备重用相同的 pointerId 取决于具体实现,因此强烈建议作者不要依赖它,而应参考 persistentDeviceId

    width

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

    height

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

    pressure

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

    tangentialPressure

    [-1,1] 范围内的归一化切向压力(也称为桶形压力),通常由附加控件(如喷枪手写笔上的指轮)设置,其中 0 是控件的中性位置。请注意,某些硬件可能仅支持 [0,1] 范围内的正值。对于不支持切向压力的硬件和平台,该值 必须0

    尽管属性名为“压力”,但在实践中,生成此属性值的硬件控件/传感器不一定对压力敏感。例如,在大多数情况下,大多数喷枪/绘画手写笔上的指轮可以自由设置,而不必要求用户对滚轮施加恒定的压力以防止其返回到零位置。
    tiltX

    Y-Z 平面与包含传感器(例如笔/手写笔)轴和 Y 轴的平面之间的平面角(以度为单位,范围为 [-90,90])。正的 tiltX 向右,朝向 X 值增加的方向。tiltX 可以与 tiltY 一起使用,以表示转换器远离数字化仪法线的倾斜度。对于不支持报告倾斜或角度的硬件和平台,该值 必须0

    tiltX explanation diagram
    3 正的 tiltX
    tiltY

    X-Z 平面与包含传感器(例如笔/手写笔)轴和 X 轴的平面之间的平面角(以度为单位,范围为 [-90,90])。正的 tiltY 朝向用户,朝向 Y 值增加的方向。tiltY 可以与 tiltX 一起使用,以表示转换器远离数字化仪法线的倾斜度。对于不支持报告倾斜或角度的硬件和平台,该值 必须0

    tiltY explanation diagram
    4 正的 tiltY
    twist

    传感器(例如笔/手写笔)绕其自身主轴的顺时针旋转(以度为单位,范围为 [0,359])。对于不支持报告扭转的硬件和平台,该值 必须0

    altitudeAngle

    传感器(例如笔/手写笔)的仰角(以弧度为单位),范围为 [0,π/2] —— 其中 0 平行于表面(X-Y 平面),π/2 垂直于表面。对于不支持报告倾斜或角度的硬件和平台,该值 必须π/2

    此处定义的 altitudeAngle 默认值为 π/2,即传感器垂直于表面的位置。这与 Touch Events - Level 2 规范对 altitudeAngle 属性的定义不同,该属性的默认值为 0
    altitudeAngle explanation diagram
    5 altitudeAngleπ/4 的示例(距离 X-Y 平面 45 度)。
    azimuthAngle

    传感器(例如笔/手写笔)的方位角(以弧度为单位),范围为 [0, 2π] —— 其中 0 代表传感器笔帽指向 X-Y 平面上 X 值增加的方向(从正上方俯视指向“3 点钟”),且值随着顺时针方向逐渐增加(π/2 在“6 点钟”,π 在“9 点钟”,3π/2 在“12 点钟”)。当传感器完全垂直于表面(altitudeAngleπ/2)时,该值 必须0。对于不支持报告倾斜或角度的硬件和平台,该值 必须0

    azimuthAngle explanation diagram
    6 azimuthAngleπ/6 的示例(“4 点钟”)。
    pointerType

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

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

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

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

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

    persistentDeviceId

    定点设备的唯一标识符。如果硬件支持多个指针,则只有当这些指针在会话期间是唯一可标识的情况下,由定点设备生成的指针事件 必须才获取 persistentDeviceId。如果指针是唯一可标识的,则分配给该定点设备的 persistentDeviceId 在会话的剩余时间内保持不变。persistentDeviceId0 必须被保留,用于指示其生成设备无法标识的事件。与 pointerId 一样,为了最大限度地减少跨页面或域进行指纹识别和跟踪的机会,persistentDeviceId 必须仅在页面/会话的生命周期内显式地与该特定定点设备关联,并且在下次新会话中再次使用该特定定点设备时,必须选择一个新的随机化 persistentDeviceId

    由于数字化仪和定点设备硬件的限制,不能保证所有来自定点设备的指针事件都能获得 persistentDeviceId。例如,设备可能无法及时向数字化仪报告其硬件 ID,以使 pointerdown 拥有 persistentDeviceId。在这种情况下,persistentDeviceId 可能最初为 0 并更改为有效值。
    getCoalescedEvents()

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

    getPredictedEvents()

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

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

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

    PointerEvent 接口继承自 UI Events 中定义的 MouseEvent。还要注意 CSSOM View Module 中的拟议扩展,它将各种坐标属性从 long 更改为 double,以允许小数坐标。对于已经为 PointerEvent(而非普通 MouseEvent)实施此拟议扩展的用户代理,在涉及 clickauxclickcontextmenu 事件 时有额外的要求。

    5.1.1 按钮状态

    5.1.1.1 和弦按钮交互

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

    相反,可以通过检查 buttonbuttons 属性的变化来检测和弦按钮按下。buttonbuttons 属性继承自 MouseEvent 接口,但在以下各节概述中更改了语义和值。

    buttonbuttons 属性的修改仅适用于指针事件。然而,对于 clickauxclickcontextmenubuttonbuttons 的值 必须遵循 [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 相同,但具有扩展的可能值集)。

    设备按钮的当前状态按钮
    鼠标移动时没有按下任何按钮,
    笔移动时悬停且没有按下任何按钮
    0
    左鼠标,
    触摸接触,
    笔接触
    1
    中鼠标4
    右鼠标,
    笔桶按钮
    2
    X1(后退)鼠标8
    X2(前进)鼠标16
    笔擦除按钮32

    5.1.2 主要指针

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

    • 在任何给定时间,每种指针类型最多只能有一个主要指针。
    • 特定指针类型第一个变为活动的指针(例如多点触控交互中第一个触摸屏幕的手指)成为该指针类型的主要指针。
    • 只有主要指针才会产生 兼容性鼠标事件。在存在多个 主要指针 的情况下,这些指针都将产生 兼容性鼠标事件
    需要单指针交互的作者可以通过忽略非主要指针来实现这一点(但是,请参见下面关于 多个主要指针 的说明)。
    当同时使用两种或多种指针设备类型时,多个指针(每种 pointerType 一个)被视为主要。例如,同时移动触摸接触和鼠标光标将产生都被视为主要指针的指针。
    某些设备、操作系统和用户代理可能会忽略同时使用一种以上类型的指针输入,以避免意外交互。例如,支持触摸和笔交互的设备可能会在使用笔时忽略触摸输入,以允许用户在使用笔时将手放在触摸屏上(这种功能通常称为“防误触”)。目前,作者还无法抑制此行为。
    在某些情况下,用户代理可能会触发没有将指针标记为主要指针的指针事件。例如,当特定类型有多个活动指针(如多点触控交互)且主要指针被移除(例如它离开屏幕)时,最终可能没有主要指针。此外,在通过设备上同一类型的所有活动指针(包括那些针对用户代理以外的应用程序的指针)来确定主要指针的平台上,如果第一个(主要)指针位于用户代理之外,而其他(非主要)指针位于用户代理之内,则 用户代理 可以为其他指针触发 isPrimary 值为 false 的指针事件。
    当前的操作系统和用户代理通常没有多个鼠标输入的概念。当存在多个鼠标设备时(例如,在同时具有触控板和外接鼠标的笔记本电脑上),所有鼠标设备通常被视为单个设备——任何设备上的移动都会转换为单个鼠标指针的移动,并且不会区分不同鼠标设备上的按钮按下。因此,通常只会有一个鼠标指针,并且该指针将是主要的。

    5.1.3 使用 PointerEvent 接口触发事件

    触发名为 e 的指针事件,意味着使用 PointerEvent 触发名为 e 的事件,其属性设置如 PointerEvent 接口和 属性和默认操作 中定义。

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

    确定触发事件的目标 如下

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

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

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

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

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

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

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

    本规范中定义的事件类型的 bubblescancelable 属性以及默认操作出现在下表中。每个这些事件类型的详细信息在 指针事件类型 中提供。

    事件类型冒泡可取消默认操作
    pointerover
    pointerenter No No
    pointerdown 变化:当指针是主要的,mousedown 事件的所有默认操作
    取消此事件也会阻止随后触发 兼容性鼠标事件
    pointermove 变化:当指针是主要的,mousemove 的所有默认操作
    pointerrawupdate No
    pointerup 变化:当指针是主要的,mouseup 的所有默认操作
    pointercancel No
    pointerout
    pointerleave No No
    gotpointercapture No
    lostpointercapture No

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

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

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

    许多用户代理在 MouseEvents 中公开非标准属性 fromElementtoElement 以支持遗留内容。我们鼓励这些用户代理将 PointerEvents 中那些(继承的)属性的值设置为 null,以引导作者使用标准化替代方案(targetrelatedTarget)。

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

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

    5.1.3.2 处理待处理指针捕获

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

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

    clickauxclickcontextmenu 事件 节中所定义,即使在 lostpointercapture 事件分发后,对应的 clickauxclickcontextmenu 事件(如有)仍将分发给捕获目标。

    5.1.3.3 抑制指针事件流

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

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

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

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

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

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

    5.1.4 由布局更改引起的边界事件

    相对于屏幕表面移动或经历其任何属性发生变化的定点设备,会根据 指针事件类型 定义触发各种事件。对于静止的定点设备(相对于屏幕表面既未移动也未经历任何属性变化的定点设备),用户代理 必须在影响了指针 命中测试 目标的布局更改后触发某些边界事件,详情请参见 pointeroverpointerenterpointeroutpointerleave用户代理 可能因性能原因(例如避免由边界事件监听器引起过多的命中测试或布局更改)延迟触发这些边界事件。

    静止的定点设备(相对于屏幕表面既未移动也未经历任何属性变化的定点设备)永远不会触发 pointermove 事件。

    5.1.5 tiltX / tiltYaltitudeAngle / azimuthAngle 之间的转换

    指针事件包括两组互补的属性,以表示转换器相对于 X-Y 平面的方向:tiltX / tiltY(在原始指针事件规范中引入),以及 azimuthAngle / altitudeAngle(采用自 Touch Events - Level 2 规范)。

    根据特定的硬件和平台,用户代理很可能只会收到一组用于转换器相对于屏幕平面方向的值——要么是 tiltX / tiltY,要么是 altitudeAngle / azimuthAngle。用户代理 必须使用以下算法来转换这些值。

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

    示例 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

    要使用 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 指针事件类型

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

    主要指针 的情况下,这些事件(gotpointercapturelostpointercapture 除外)也可能触发 兼容性鼠标事件

    5.3.1 pointerover 事件

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

    5.3.2 pointerenter 事件

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

    • 确定指针目标的步骤检测到指针已移入某个元素或其后代元素之一。
    • 一个布局变更导致某个元素或其后代元素之一的命中测试边界移动到固定定点设备的未捕获指针下方。
    • 在用户代理为不支持悬停的设备触发 pointerdown 事件之前(参见 pointerdown)。
    该事件类型与 pointerover 相似,但有两点不同:pointerenter 不会冒泡,且其分发会考虑包括后代元素在内的命中测试边界。
    此事件类型与 [UIEVENTS] 中描述的 mouseenter 事件,以及 [CSS21] 中描述的 CSS :hover 伪类之间存在相似之处。另请参见 pointerleave 事件。

    5.3.3 pointerdown 事件

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

    对于鼠标(或其他多按钮指针设备),这意味着 pointerdownpointerup 并非在与 mousedownmouseup 完全相同的情况下触发。有关更多信息,请参见和弦按钮

    对于不支持悬停的输入设备,在分发 pointerdown 事件之前,用户代理还必须触发一个指针事件,其名称为 pointerover,随后紧跟一个名为 pointerenter 的指针事件。

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

    5.3.4 pointermove 事件

    当指针更改了任何不触发 pointerdownpointerup 事件的属性时,用户代理必须触发一个指针事件,其名称为 pointermove。这包括对坐标、压力、切向压力、倾斜、扭转、接触几何形状(widthheight)或和弦按钮的任何更改。

    用户代理可以延迟分发 pointermove 事件(例如,出于性能原因)。合并事件信息将通过单个已分发的 pointermove 事件的 getCoalescedEvents 方法公开。此类事件的最终坐标应被用于查找事件目标。

    5.3.5 pointerrawupdate 事件

    当指针更改了任何不触发 pointerdownpointerup 事件的属性时,用户代理必须触发一个名称为 pointerrawupdate 的指针事件,且仅在安全上下文内触发。有关此类属性的列表,请参阅 pointermove 事件。

    pointermove 不同,用户代理在 JavaScript 能够处理事件时,尽可能快且尽可能频繁地分发 pointerrawupdate 事件。

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

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

    关于 pointerrawupdatepointermove 的顺序,如果用户代理收到了来自平台导致同时触发 pointerrawupdatepointermove 事件的更新,则用户代理必须在相应的 pointermove 之前分发 pointerrawupdate 事件。

    除了 target 外,自上次 pointermove 事件以来所有已分发的 pointerrawupdate 事件的合并事件列表连接,在其他事件属性方面与下一个 pointermove 事件的合并事件相同。pointerrawupdate 的属性大多与 pointermove 相同,除了 cancelable,对于 pointerrawupdate 来说,该属性必须为 false。

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

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

    5.3.6 pointerup 事件

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

    对于不支持悬停的输入设备,用户代理在分发 pointerup 事件后,还必须触发一个指针事件,其名称为 pointerout,随后紧跟一个名为 pointerleave 的指针事件。

    所有 pointerup 事件的 pressure 值均为 0

    如果指针当前已被捕获,用户代理还必须隐式释放指针捕获

    对于鼠标(或其他多按钮指针设备),这意味着 pointerdownpointerup 并非在与 mousedownmouseup 完全相同的情况下触发。有关更多信息,请参见和弦按钮

    5.3.7 pointercancel 事件

    用户代理检测到需要抑制指针事件流的情况时,必须触发一个指针事件,其名称为 pointercancel

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

    5.3.8 pointerout 事件

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

    5.3.9 pointerleave 事件

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

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

    5.3.10 gotpointercapture 事件

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

    5.3.11 lostpointercapture 事件

    在指针的捕获被释放后,用户代理必须触发一个指针事件,其名称为 lostpointercapture。此事件必须在捕获释放后,该指针的任何后续事件之前触发。该事件在被移除指针捕获的元素上触发。除了 clickauxclickcontextmenu 事件外,该指针的所有后续事件都遵循常规的命中测试机制(超出本规范范围)来确定事件目标。参见释放指针捕获隐式释放指针捕获处理挂起的指针捕获章节。

    5.3.12 clickauxclickcontextmenu 事件

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

    这些事件必须PointerEvent 类型,并受本节其余部分提到的额外要求约束。

    5.3.12.1 事件属性

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

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

    PointerEvent 中所述,CSSOM 视图模块建议将各种坐标属性(screenXscreenYpageXpageYclientXclientYxyoffsetXoffsetY)重新定义为 double 类型,以允许小数坐标。然而,当此更改仅应用于 PointerEvent,而不应用于常规 MouseEvent 时,已被证明在 clickauxclickcontextmenu 的情况下会导致与遗留代码的 Web 兼容性问题。因此,仅为 PointerEvent 实现了 CSSOM 视图模块中所提建议更改的用户代理必须使用 Math.floor [ECMASCRIPT] 将 clickauxclickcontextmenu 的各种坐标属性转换为 long 值(如原始 UI 事件中所定义)。

    5.3.12.3 事件分发

    clickauxclickcontextmenu 事件必须遵循 [UIEVENTS] 规范中定义的分发过程,除非事件目标被覆盖,如下算法所示:

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

      事件 userEvent 可以是非 PointerEvent;例如,当通过点击 checkbox 元素上的空格键导致分发 click 事件时,它就是 KeyboardEvent

      userEventPointerEvent 时,userEventclickauxclick 事件的 pointerup,或者对于 contextmenu 事件,是 pointerdownpointerup 事件(取决于原生平台约定)。

    2. 如果 userEvent 不是 PointerEvent,请遵循 [UIEVENTS] 规范分发 event,而不要覆盖 event 目标,并跳过以下剩余步骤。
    3. 按如下定义 target

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

      否则(eventclickauxclick 事件,且 userEvent 是在未捕获状态下分发的 pointerup 事件),令 target 为在 event 分发瞬间 DOM 中相应 pointerdownpointerup 目标的最近共同包含祖先。

    4. 遵循 [UIEVENTS] 规范将 event 分发给 target

      如果 userEvent 被捕获,event 将被分发给 userEvent 的捕获目标,即使具有相同 pointerIdlostpointercapture 事件已被分发。

    6. 滚轮事件 (Wheel Events)

    滚轮是可以沿一个或多个空间维度旋转,并可以与定点设备关联的设备。坐标系取决于环境配置。

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

    WheelEvent 对象的 deltaX、deltaY 和 deltaZ 属性指示沿其相应轴的测量值,单位为像素、行或页。报告的测量值是在特定于环境的算法将滚轮设备的实际旋转/移动转换为适当的值和单位之后提供的。

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

    deltaX、deltaY 和 deltaZ 属性值的符号(正或负)在实际滚轮设备沿相同方向旋转/移动时,在 wheel 事件的多次分发之间必须保持一致。如果用户代理将滚动作为 wheel 事件的默认操作,则增量的符号由右手坐标系给出,其中正 X、Y 和 Z 轴分别指向文档的最右侧边缘、最底部边缘和最深处(远离用户)。

    个别用户代理可以(取决于其环境和硬件配置)对滚轮上的相同物理用户交互进行不同的解释。例如,在触控板边缘从上到下的垂直滑动可以被解释为滚轮操作,旨在向下滚动页面或向上平移页面(即分别导致正或负的 deltaY 值)。当触发第一个滚轮事件时,用户代理必须创建一个滚轮事件事务,以便在实现指定的有限时间内后续的所有滚轮事件都可以针对同一元素。一个滚轮事件事务是与单个用户手势相关联的一系列滚轮事件。滚轮事件事务必须具有关联的事件目标,该目标是发生组中第一个滚轮事件时的最顶层事件目标如果一系列针对可滚动元素的滚轮事件起始于子元素上方,则同一用户手势的后续事件可能会在子元素上方发生。

    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
    增量的测量单位必须为像素。这是大多数操作系统和实现配置中最典型的情况。
    DOM_DELTA_LINE
    增量的测量单位必须为单独的文本行。这在许多表单控件中是这种情况。
    DOM_DELTA_PAGE
    增量的测量单位必须为页,定义为单个屏幕或划分的页面。
    deltaX
    wheel 事件的默认操作是滚动的用户代理中,该值必须是事件未被取消时沿 x 轴滚动的测量值(单位为像素、行或页)。否则,这是围绕 x 轴移动滚轮设备的特定于实现的测量值(单位为像素、行或页)。该属性的未初始化值必须0.0
    deltaY
    wheel 事件的默认操作是滚动的用户代理中,该值必须是事件未被取消时沿 y 轴滚动的测量值(单位为像素、行或页)。否则,这是围绕 y 轴移动滚轮设备的特定于实现的测量值(单位为像素、行或页)。该属性的未初始化值必须0.0
    deltaZ
    wheel 事件的默认操作是滚动的用户代理中,该值必须是事件未被取消时沿 z 轴滚动的测量值(单位为像素、行或页)。否则,这是围绕 z 轴移动滚轮设备的特定于实现的测量值(单位为像素、行或页)。该属性的未初始化值必须0.0
    deltaMode
    deltaMode 属性包含对增量值测量单位的指示。默认值为 DOM_DELTA_PIXEL(像素)。此属性必须设置为 DOM_DELTA 常量之一,以指示增量值的测量单位。精确的测量值特定于设备、操作系统和应用程序配置。该属性的未初始化值必须0

    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 属性的 WheelEvent 对象,设置为枚举值 0、1 或 2,如果滚轮的旋转导致滚动,则分别表示滚动的像素量(DOM_DELTA_PIXEL)、滚动行数(DOM_DELTA_LINE)或滚动页数(DOM_DELTA_PAGE)。

    6.2 滚轮事件类型

    6.2.1 wheel

    类型 wheel
    Interface WheelEvent
    同步 / 异步 异步
    冒泡
    受信任的目标 元素
    可取消 不同
    可组合(Composed)
    默认操作 滚动(或缩放)文档
    上下文
    (受信任事件)
    • 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 属性的单位指示器(像素、行或页)
    当鼠标滚轮绕任何轴旋转,或等效输入设备(如鼠标球、某些平板电脑或触摸板等)模拟了此类操作时,用户代理必须分发此事件。根据平台和输入设备,对角线滚轮增量可以作为单个具有多个非零轴的 wheel 事件,或作为每个非零轴的单独 wheel 事件传递。wheel 事件类型的典型默认操作是以指示的量滚动(或在某些情况下,缩放)文档。如果该事件被取消,实现绝不能滚动或缩放文档(或执行与此事件类型关联的任何其他特定于实现的默认操作)。

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

    6.2.2 滚轮事件的可取消性

    在滚轮事件上调用 preventDefault 可以阻止或中断滚动。为了获得最大滚动性能,用户代理可能不会等待与滚动关联的每个滚轮事件处理完毕以查看其是否会被取消。在这种情况下,用户代理应该生成 wheel 事件,其 cancelable 属性为 false,表明 preventDefault 不能用于阻止或中断滚动。否则,cancelable 将为 true

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

    7. Element 接口的扩展

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

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

    为参数 pointerId 所标识的指针,在调用此方法的元素上设置指针捕获。对于该指针的后续事件,捕获目标将替代常规的命中测试结果,如同指针始终在该捕获目标上方一样,并且直到捕获被释放,它们必须始终针对该元素。指针必须处于其活动按钮状态才能使此方法生效,否则它将静默失败。当所提供方法参数与任何活动指针不匹配时,抛出 "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. 返回设备支持的同时触摸接触的最大数量。对于具有多个数字化仪的设备(例如多个触摸屏),该值必须是每个单独数字化仪所支持的最大接触数集合中的最大值。

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

    虽然 maxTouchPoints 值大于 0 表明用户的设备具备支持触摸输入的能力,但这并不一定意味着用户使用触摸输入。作者应注意同时考虑系统上可能存在的其他输入形式,如鼠标、触控笔或屏幕阅读器。
    maxTouchPoints 常用于确保当前硬件能够识别内容的交互模型。可以为具备能力较弱硬件的用户提供 UI 引导。在无法获知确切触摸点数量的平台上,提供的是保证能够被识别的最小数量。因此,已识别的触摸点数量可能会超过 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 属性决定了直接操作交互(尽管属性名称如此,但并不限于触摸)是否可以触发用户代理的平移和缩放行为。参见关于 touch-action的章节。

    在开始平移或缩放之前,如果满足以下所有条件,用户代理必须抑制指针事件流

    • 用户代理已确定(通过超出本规范范围的方法)直接操作交互将用于平移或缩放,
    • 已为指针发送了 pointerdown 事件,并且
    • 尚未为该指针发送(紧随上述 pointerdown 的)pointeruppointercancel 事件。
    一些用户代理为涉及一系列单独离散手势的行为实现了复杂手势,但这些行为都被视为单个连续手势的一部分。例如,考虑触摸屏上的“甩动以滚动”手势:用户以快速的手指移动开始平移文档,从触摸屏上抬起手指,文档继续以模拟的惯性平移。在文档仍在移动时,用户可能会将手指放在触摸屏上并执行另一次“甩动”以提供平移的进一步动力,或者抵消当前的平移以减慢速度、完全停止平移或反转平移的方向。由于本规范没有规范地定义如何实现手势和行为,因此是否在第二次触摸(在将其解释为第二次“甩动”或抵消当前平移之前)触发指针事件由用户代理自行决定。
    touch-action 不适用于/级联到嵌入的浏览上下文中。例如,即使将 touch-action 应用于 <iframe>,也不会对 <iframe> 内部平移和缩放的直接操作交互行为产生任何影响。

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

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

    • 如果平移和缩放的直接操作交互在元素的坐标空间中是被允许的,则该交互符合元素的 touch-action。请注意,如果应用了 CSS 变换,元素的坐标空间可能与屏幕坐标有所不同,从而影响此处的符合性;例如,相对于屏幕旋转 90 度的元素的 X 轴将平行于屏幕坐标的 Y 轴。
    • 如果平移的直接操作交互符合命中测试元素与其最近的包含祖先(该祖先必须是一个滚动容器,如 [CSS-OVERFLOW-3] 中定义)之间每个元素的 touch-action 属性,则该交互得到支持。
    • 如果缩放的直接操作交互符合命中测试元素与顶级浏览上下文document 元素(如 [HTML] 中定义)之间每个元素的 touch-action 属性,则该交互得到支持。
    • 一旦平移或缩放已开始,且用户代理已确定手势是否应作为用户代理直接操作行为处理,则 touch-action 相关值的任何更改在操作期间都将被忽略。例如,在 pointerdown 处理器脚本中,以编程方式将元素的 touch-action 值从 auto 更改为 none,并不会导致用户代理在指针处于活动状态期间放弃或抑制针对该输入的任何平移或缩放行为。
    • 类似地,对于 pan-* 的各种 touch-action 值,一旦用户代理在手势开始时确定是否直接处理手势,在该指针处于活动状态期间,同一手势方向的后续变化被用户代理忽略。例如,如果元素已设置为 touch-action: pan-y(意味着只有垂直平移由用户代理处理),而触摸手势从水平开始,如果用户在手指仍然触摸屏幕时将手势方向更改为垂直,则不应发生垂直平移。
    一些用户代理支持涉及多个并发指针(例如多点触控)的平移和缩放交互。处理或关联多个并发指针的 touch-action 值的方法超出了本规范的范围。

    10.3 touch-action 值详情

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

    术语“平移”和“滚动”被视为同义词(或更确切地说,“平移”是使用直接操作输入进行的“滚动”)。定义用于触发平移/滚动,或触发 autonone 值的行为的交互或手势,超出了本规范的范围。
    auto
    用户代理可以考虑在元素上开始的任何与视口平移和缩放相关的允许直接操作行为。
    none(无)
    在元素上开始的直接操作交互绝不能触发与视口平移和缩放相关的行为。
    pan-x
    pan-left
    pan-right
    pan-y
    pan-up
    pan-down
    用户代理可以仅考虑在元素上开始的,且平移起始方向在所有列出值指定的任何方向内的直接操作交互。一旦平移开始,即使在反转方向开始的平移是不被允许的,用户仍可以反转方向。相反,当平移被限制为单个轴(例如使用 pan-xpan-y)时,平移期间无法更改轴。
    manipulation
    用户代理可以仅考虑在元素上开始的,且仅用于平移和连续缩放(如捏合缩放)的直接操作交互,但绝不能触发其他依赖于在设定时间内必须发生的多次激活的相关行为(如双击缩放,或双击并保持以进行单指缩放)。
    实作中常见的其他 touch-action在 [COMPAT] 中定义。
    touch-action 属性仅适用于同时支持 CSS widthheight 属性的元素(参见 [CSS21])。此限制旨在促进用户代理针对低延迟直接操作(平移和缩放)进行优化。对于默认不支持的元素(例如作为非替换内联元素<span>),作者可以将 display CSS 属性设置为支持 widthheight 的值(如 block)。未来的规范可能会将此 API 扩展到所有元素。

    特定于方向的平移值对于自定义某些滚动越界(overscroll)行为非常有用。例如,要实现简单的“下拉刷新”效果,当滚动位置为 0 时,可以将文档的 touch-action 设置为 pan-x pan-down,否则设置为 pan-x pan-y。这允许指针事件处理器定义从文档顶部开始的向上平移/滚动行为。

    特定于方向的平移值还可用于组合组件,这些组件在支持原生滚动的元素内(或反之)通过指针事件处理实现自定义平移。例如,图片轮播组件可以使用 pan-y,以确保其接收任何水平平移操作的指针事件,且不会干扰文档的垂直平移。当轮播组件到达最右侧边界时,它可以将其 touch-action 更改为 pan-y pan-right,以便随后在其边界之外的滚动操作能够在视口范围内滚动文档(如果可能的话)。在平移/滚动操作进行时,无法更改其行为。

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

    11. 指针捕获 (Pointer capture)

    11.1 简介

    本节是非规范性的。

    指针捕获允许将特定指针的事件(包括任何兼容鼠标事件)重定向到特定元素,而不是指针位置的常规命中测试 (hit test)结果。这在自定义滑动条控件(例如类似于 [HTML] 中的 <input type="range"> 控件)等场景中非常有用。指针捕获可以设置在滑动条的滑块元素上,允许用户即使在指针滑出滑块区域时,也能继续来回滑动该控件。

    Custom Volume Slider
    7 通过来回滑动滑块元素来选择值的自定义滑动条控件示例。在滑块上触发 pointerdown 后,可以使用指针捕获来允许用户即使在指针移出滑块时也能继续滑动它。

    11.2 设置指针捕获

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

    1. 如果作为方法参数提供的 pointerId 与任何活动指针不匹配,则抛出一个 "NotFoundError" DOMException
    2. 指针为给定 pointerId 所指定的活动指针
    3. 如果该元素连接 [DOM],则抛出一个 "InvalidStateError" DOMException
    4. 如果调用此方法时,该元素节点文档 [DOM] 具有一个锁定元素([PointerLock] pointerLockElement),则抛出一个 "InvalidStateError" DOMException
    5. 如果该指针不在活动按钮状态,或者该元素节点文档不是该指针活动文档,则终止这些步骤。
    6. 对于指定的 pointerId,将待处理指针捕获目标覆盖 (pending pointer capture target override) 设置为调用此方法的 Element
    如果在设置或释放指针捕获的调用处于挂起状态时(参见处理待处理指针捕获),进行了另一次设置或释放调用,则如果第二次调用成功,它将覆盖第一次调用;否则,第一次调用保持有效。对于在 pointerdown 监听器处释放隐式指针捕获的失败尝试,此规则同样适用。

    11.3 释放指针捕获

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

    1. 如果作为方法参数提供的 pointerId 与任何活动指针不匹配,且这些步骤并非因隐式释放指针捕获而调用,则抛出一个 "NotFoundError" DOMException
    2. 如果该 Element 对于指定的 pointerIdhasPointerCapture 为 false,则终止这些步骤。
    3. 对于指定的 pointerId,清除待处理指针捕获目标覆盖(如果已设置)。
    参见设置指针捕获部分的说明。

    11.4 隐式指针捕获

    实现用于平移和缩放的直接操作交互的输入(例如触摸屏上的触摸或触控笔)应当表现得就像在调用任何 pointerdown 监听器之前,在目标元素上调用了 setPointerCapture 一样。hasPointerCapture API 可用于(例如在 pointerdown 监听器中)确定是否发生了这种情况。如果在下一个指针事件触发之前未调用 releasePointerCapture,则会向目标分发一个 gotpointercapture 事件(正常情况下),指示捕获处于活动状态。

    这是相对于 [PointerEvents] 的一个破坏性变更,但不会影响绝大多数现有内容。除了符合典型的平台用户体验约定外,这种隐式捕获的设计使用户代理能够进行性能优化,避免在触摸移动事件上调用命中测试,而无需开发者显式启用(这与现有的主流原生和 Web 触摸输入 API 的性能属性一致)。
    此外,用户代理可以针对特定 UI 小部件(如输入范围控件)上的所有输入设备实现隐式指针捕获行为(允许在交互过程中手指移动到表单控件本身之外)。

    11.5 隐式释放指针捕获

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

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

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

    前两个段落的结果是,在移除捕获节点后的下一次处理待处理指针捕获期间,会向文档触发一个对应于被捕获指针的 lostpointercapture 事件。

    当一个元素成功应用了指针锁定 [PointerLock] 时,用户代理必须执行相应的步骤,就如同已经调用了 releasePointerCapture 方法,如果此时有任何元素被捕获或处于待捕获状态。

    12. 合并事件与预测事件

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

    12.1 合并事件 (Coalesced events)

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

    Close-up view of a curve, showing coalesced and un-coalesced points
    8 绘图应用程序中的曲线示例——仅使用 pointermove 事件中的合并坐标(灰色圆点),曲线明显呈角状且锯齿状;使用 getCoalescedEvents() 提供的更细粒度的点绘制相同的线条(红色圆圈),可以得到更平滑的指针运动近似值。

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

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

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

    示例 14:使用合并事件列表的基本 Canvas 绘图应用程序
    <style>
        /* Disable intrinsic user agent direct manipulation behaviors (such as panning or zooming)
        so that all events on the canvas element are given to the application instead. */
    
        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); // Paint all raw/non-coalesced points
                }
            } else {
                paint(e); // Paint the final coalesced point
            }
        });
    
        function paint(event) {
            if (event.buttons>0) {
                context.fillRect(event.clientX, event.clientY, 5, 5);
            }
        }
    
    </script>
    PointerEvent 的属性将以最能代表合并事件列表中的事件的方式进行初始化。用户代理应采取的具体方法不在本规范涵盖范围内。

    所有这些分发事件的顺序必须与原始事件的实际顺序相匹配。例如,如果一个 pointerdown 事件导致了合并的 pointermove 事件的分发,用户代理必须首先分发一个包含所有该 pointerId 的合并事件的 pointermove 事件,随后分发该 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 预测事件 (Predicted events)

    一些用户代理具有内置算法,在经过一系列确认的指针移动后,可以进行预测(基于当前手势的前序事件以及移动的速度/轨迹),预测未来的指针移动位置可能在哪里。应用程序可以使用此信息结合 getPredictedEvents 方法,推测性地“超前绘制”到预测位置以减少感知延迟,并在实际点收到后丢弃这些预测点。

    A line drawn using coalesced points, showing predicted future points
    9 绘图应用程序中的线条示例(从左下角到右上角的绘图手势结果),使用 pointermove 事件中的合并坐标,显示用户代理预测的未来点(灰色圆圈)。

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

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

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

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

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

    请注意,作者应仅将预测事件视为有效的预测,直到下一个指针事件被分发为止。根据用户代理预测未来事件的程度,可能会出现常规指针事件比一个或多个预测事件的时间戳更早分发的情况。

    示例 15:使用合并事件和预测事件进行绘图的概念方法
    
    let predicted_points = [];
    window.addEventListener("pointermove", function(event) {
        // Clear the previously drawn predicted points.
        for (let e of predicted_points.reverse()) {
            clearPoint(e.pageX, e.pageY);
        }
    
        // Draw the actual movements that happened since the last received event.
        for (let e of event.getCoalescedEvents()) {
            drawPoint(e.pageX, e.pageY);
        }
    
        // Draw the current predicted points to reduce the perception of latency.
        predicted_points = event.getPredictedEvents();
        for (let e of predicted_points) {
            drawPoint(e.pageX, e.pageY);
        }
    });
    

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

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

    1. 将事件的 pointerIdpointerTypeisPrimaryisTrusted 设置为与“父级”指针事件的相应属性相匹配。
    2. 将事件的 cancelablebubbles 设置为 false(因为这些事件绝不会单独分发)。
    3. 将事件的 合并事件列表预测事件列表 设置为空列表。
    4. 将所有其他属性初始化为默认的 PointerEvent 值。

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

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

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

    现有的绝大多数 Web 内容仅使用鼠标事件进行编码。以下描述了一种算法,说明用户代理可以如何将通用指针输入映射到鼠标事件,以实现与此类内容的兼容。

    与鼠标事件的兼容性映射是本规范的一项可选功能。鼓励用户代理支持此功能,以实现与现有遗留内容的最大兼容性。

    从高层次来看,兼容鼠标事件旨在与它们各自的指针事件“交错”分发。但是,这种特定的顺序并非强制性的,且实现了兼容鼠标事件的用户代理可以决定延迟或合并鼠标事件的分发,只要它们的相对顺序是一致的。

    特别是在触摸屏输入的情况下,用户代理可以应用额外的手势识别启发式方法(除非作者通过 touch-action 显式禁止)。在 pointerdown 事件和 pointerup 事件之间的事件序列期间,手势识别可能必须等到 pointerup 事件才能检测或忽略手势。因此,如果用户代理确定交互不被视为特定手势,则整个序列的兼容鼠标事件可能会在最后一个 pointerup 事件之后一起分发。这些用户代理手势识别的具体细节未在本规范中定义,且不同实现之间可能存在差异。

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

    某些高级事件(如 contextmenufocusblur)与指针事件之间的相对顺序是未定义的,并且在不同用户代理之间存在差异。例如,在某些用户代理中,contextmenu 通常会在 pointerup 之后,而在另一些中,它通常会在 pointeruppointercancel 之前,在某些情况下,它可能会在没有任何相应指针事件的情况下触发(例如,作为键盘交互的结果)。

    此外,用户代理可能会应用其自己的启发式方法来确定是否应触发 clickauxclickcontextmenu 事件。如果存在相同类型的其他(非主要)指针或不同类型的其他主要指针,一些用户代理可能选择不触发这些事件。用户代理可能会确定特定的操作不是一个“干净”的点击、单击或长按(例如,如果触摸屏上的手指交互在手指接触屏幕时包含了过多的移动),并决定不触发 clickauxclickcontextmenu 事件。用户代理行为的这些方面未在本规范中定义,且不同实现之间可能存在差异。

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

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

    鼠标事件只能在指针处于按下状态时被阻止。悬停的指针(例如未按下任何按钮的鼠标)无法阻止其鼠标事件。

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

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

    13.1 跟踪遗留鼠标指针的有效位置

    虽然只有主要指针才能产生兼容鼠标事件,但多个主要指针可以同时处于活动状态,每个指针产生其自己的兼容鼠标事件。为了与依赖 MouseEvents 的脚本兼容,鼠标转换事件(mouseovermouseoutmouseentermouseleave应当模拟单个遗留鼠标输入的移动。这意味着根据 [UIEVENTS],每个事件目标的进入/离开状态是有效的。用户代理应当通过在文档中维护遗留鼠标指针的有效位置来保证这一点,具体如下。

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

    1. T 为正在分发的 pointerdownpointeruppointermove 事件的目标。对于 pointerleave 事件,取消设置 T
    2. 如果 T 和当前的遗留鼠标指针的有效位置均未设置或相等,则终止这些步骤。
    3. 根据 [UIEVENTS],针对从当前遗留鼠标指针的有效位置移动到 T 的鼠标,分发 mouseovermouseoutmouseentermouseleave 事件。将当前遗留鼠标指针的有效位置T 的未设置值视为窗口外的鼠标位置。
    4. 遗留鼠标指针的有效位置设置为 T

    遗留鼠标指针的有效位置模拟了这样一个事实:我们不能总是将指针转换事件(pointeroverpointeroutpointerenterpointerleave)直接映射到相应的遗留鼠标转换事件(mouseovermouseoutmouseentermouseleave)。以下动画说明了一个用户代理需要分发比指针转换事件更多的遗留鼠标转换事件的情况,以便能够协调两个使用单个遗留鼠标输入的主要指针。

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

    在此动画中,请注意鼠标点击和触摸点击之间的时间段。按钮 1 没有接收到 pointerout 事件(因为“真实”鼠标指针在此期间没有离开按钮矩形),但当遗留鼠标指针的有效位置在触摸点击时移动到按钮 2 时,按钮 1 会接收到一个 mouseout 事件。同样,在触摸点击和鼠标离开按钮 1 之前的时间段内,按钮 1 出于同样的原因没有接收到 pointerover 事件,但当遗留鼠标指针的有效位置移回按钮 1 内部时,按钮 1 会接收到一个 mouseover 事件。

    13.2 支持悬停设备的映射

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

    1. 如果待分发的指针事件的 isPrimary 属性为 false,则分发该指针事件并终止这些步骤。
    2. 如果待分发的指针事件是 pointerdownpointeruppointermove 事件,或者在 window 上分发的 pointerleave 事件,则按跟踪遗留鼠标指针的有效位置中所述分发兼容鼠标转换事件。
    3. 分发指针事件。
    4. 如果分发的指针事件是 pointerdown 且事件的已取消标志已设置,则为此 pointerType 设置 PREVENT MOUSE EVENT 标志。
    5. 如果未为此 pointerType 设置 PREVENT MOUSE EVENT 标志,且分发的指针事件是:
    6. 如果分发的指针事件是 pointeruppointercancel,则清除此 pointerTypePREVENT MOUSE EVENT 标志。

    13.3 不支持悬停设备的映射

    某些设备(例如大多数触摸屏)不支持在不处于活动状态时悬停坐标(或坐标集)。许多编码为鼠标事件的现有内容假设鼠标正在产生事件,因此某些质量通常是真的:

    • 输入可以在不激活的情况下独立悬停(例如,在未按下任何按钮的情况下移动鼠标光标)。
    • 输入很可能在点击元素之前在其上产生 mousemove 事件。
    悬停有时用于在为鼠标设计的内容中切换 UI 元素的可见性(例如“悬停菜单”)。此内容通常与不支持悬停的设备不兼容。本规范未定义与此场景兼容的映射或行为。它将在本规范的未来版本中考虑。

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

    1. 如果待分发的指针事件的 isPrimary 属性为 false,则分发该指针事件并终止这些步骤。
    2. 如果待分发的指针事件是 pointerover 且此指针的 pointerdown 事件尚未分发,则触发 mousemove 事件(为了与遗留的鼠标特定代码兼容)。
    3. 如果待分发的指针事件是 pointerdownpointeruppointermove 事件,或者在 window 上分发的 pointerleave 事件,则按跟踪遗留鼠标指针的有效位置中所述分发兼容鼠标转换事件。
    4. 分发指针事件。
    5. 如果分发的指针事件是 pointerdown 且事件的已取消标志已设置,则为此 pointerType 设置 PREVENT MOUSE EVENT 标志。
    6. 如果未为此 pointerType 设置 PREVENT MOUSE EVENT 标志,且分发的指针事件是:
    7. 如果分发的指针事件是 pointeruppointercancel,则清除此 pointerTypePREVENT MOUSE EVENT 标志。

    如果用户代理同时支持触摸事件(如 [TOUCH-EVENTS] 中所定义)和指针事件,则用户代理绝不能同时生成本节所述的兼容鼠标事件,以及 [TOUCH-EVENTS] 中概述的回退鼠标事件

    使用不支持悬停的主要指针激活元素(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 和事件实现所引起的安全和隐私问题。

    本规范中定义的许多事件类型都是响应用户操作而分发的。这使得恶意的事件监听器能够访问用户通常认为机密的信息,例如用户在与页面交互时鼠标/触控笔/手指的确切路径/移动。

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

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

    作为缓解措施,用户代理可以考虑包含允许用户禁用对特定传感器数据(如角度、倾斜、压力)的访问的能力,和/或仅在获得用户明确许可后才使其可用。

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

    除了这些考虑因素外,工作组认为本规范:

    • 不暴露个人身份信息。
    • 不处理高价值数据。
    • 不为在浏览会话中持久存在的源引入新状态。
    • 不向 Web 暴露持久的、跨源的状态。
    • 不向源暴露其当前无法访问的任何其他数据。
    • 不启用新的脚本执行/加载机制。
    • 不允许源访问用户的位置。
    • 不需要在用户代理处于“隐身”模式时进行任何特殊处理。
    • 不允许源访问其他设备。
    • 不允许源控制用户代理的原生 UI。
    • 不向 Web 暴露临时标识符。
    • 不区分第一方和第三方上下文中的行为。
    • 不将数据持久化到用户的本地设备。
    • 不允许降低默认的安全特性。

    15. 术语表

    本节是非规范性的。

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

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

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

    指针 (pointer)
    与硬件无关的输入设备表示,可以针对屏幕上的特定坐标(或坐标集),例如鼠标、笔或触摸接触。
    旋转 (rotation)
    使用 WheelEvent 接口的输入设备上增量变化的指示。在某些设备上,这可能是滚轮的字面旋转,而在另一些设备上,它可能是沿平面的移动或在特定按钮上的压力。
    最顶层事件目标 (topmost event target)
    最顶层事件目标必须是渲染顺序中最高且能够作为目标的元素。在图形用户界面中,这是用户指向设备下的元素。用户界面的“命中测试”设施用于确定目标。有关命中测试和堆叠顺序的具体细节,请参阅宿主语言

    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() 相同的行为。
    Warning

    initMouseEvent 方法已弃用,但为了向后兼容广泛部署的实现而提供支持。

    DOMString typeArg
    有关此参数的描述,请参阅 initEvent() 方法。
    boolean bubblesArg
    有关此参数的描述,请参阅 initEvent() 方法。
    boolean cancelableArg
    有关此参数的描述,请参阅 initEvent() 方法。
    Window? viewArg
    指定 view。此值可能null
    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。此值可能null

    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 级 (CSS Overflow Module Level 3)。Elika Etemad; Florian Rivoal。W3C。2025 年 10 月 7 日。W3C 工作草案。URL: https://w3org.cn/TR/css-overflow-3/
    [css-position]
    CSS 定位布局模块第 3 级 (CSS Positioned Layout Module Level 3)。Elika Etemad; Tab Atkins Jr。W3C。2025 年 10 月 7 日。W3C 工作草案。URL: https://w3org.cn/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://w3org.cn/TR/CSS2/
    [CSSOM-View]
    CSSOM 视图模块. Simon Fraser; Emilio Cobos Álvarez. W3C. 2025年9月16日. W3C 工作草案. URL: https://w3org.cn/TR/cssom-view-1/
    [DOM]
    DOM Standard. Anne van Kesteren. WHATWG. Living Standard. URL: https://dom.spec.whatwg.org/
    [ECMASCRIPT]
    ECMAScript 语言规范. Ecma International. 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.whatwg.cn/multipage/
    [HTML401]
    HTML 4.01 规范 (HTML 4.01 Specification)。Dave Raggett; Arnaud Le Hors; Ian Jacobs。W3C。2018 年 3 月 27 日。W3C 推荐标准。URL: https://w3org.cn/TR/html401/
    [infra]
    Infra Standard. Anne van Kesteren; Domenic Denicola. WHATWG. Living Standard. URL: https://infra.spec.whatwg.org/
    [PointerEvents]
    指针事件 (Pointer Events)。Patrick Lauke; Robert Flack。W3C。2026 年 2 月 11 日。W3C 工作草案。URL: https://w3org.cn/TR/pointerevents4/
    [PointerLock]
    指针锁定 2.0 (Pointer Lock 2.0)。Mustaq Ahmed; Vincent Scheib。W3C。2026 年 2 月 25 日。W3C 工作草案。URL: https://w3org.cn/TR/pointerlock-2/
    [RFC2119]
    Key words for use in RFCs to Indicate Requirement Levels. S. Bradner. IETF. 1997年3月. Best Current Practice. 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]
    触摸事件 (Touch Events)。Doug Schepers; Sangwhan Moon; Matt Brubeck; Arthur Barstow。W3C。2013 年 10 月 10 日。W3C 推荐标准。URL: https://w3org.cn/TR/touch-events/
    [UIEVENTS]
    UI 事件. Xiaoqian Wu. W3C. 2026年2月21日. W3C 工作草案. URL: https://w3org.cn/TR/uievents/
    [UIEvents-Key]
    UI 事件 KeyboardEvent 按键值 (UI Events KeyboardEvent key Values)。Travis Leithead; Gary Kacmarcik。W3C。2025 年 4 月 22 日。W3C 推荐标准。URL: https://w3org.cn/TR/uievents-key/
    [WEBDRIVER-BIDI]
    WebDriver 双向协议 (WebDriver BiDi)。James Graham; Alex Rudenko; Maksim Sadym。W3C。2026 年 2 月 25 日。W3C 工作草案。URL: https://w3org.cn/TR/webdriver-bidi/
    [WEBIDL]
    Web IDL Standard. Edgar Chen; Timothy Gu. WHATWG. Living Standard. 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 年 2 月 13 日。CRD(候选推荐草案)。URL: https://w3org.cn/TR/pointerevents3/
    [WCAG22]
    Web 内容无障碍指南 (WCAG) 2.2。Michael Cooper; Andrew Kirkpatrick; Alastair Campbell; Rachael Bradley Montgomery; Charles Adams。W3C。2024 年 12 月 12 日。W3C 推荐标准。URL: https://w3org.cn/TR/WCAG22/