指针事件

第 4 级

W3C 工作草案

关于此文档的更多详细信息
此版本:
https://www.w3.org/TR/2026/WD-pointerevents4-20261008/
最新发布版本:
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/pointerevents3
编辑:
Patrick H. Lauke (TetraLogical)
Robert Flack (Google)
前任编辑:
Matt Brubeck (Mozilla)
Rick Byers (Google)
Navid Zolghadr (Google)
反馈:
GitHub w3c/pointerevents (拉取请求, 新建议题, 开放议题)
public-pointer-events@w3.org 主题行使用 [pointerevents4] … 消息主题 …(存档)
浏览器支持:
caniuse.com

摘要

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

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

本文档的状态

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

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

本次修订包含以下新功能:

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

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

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

本文档由一个依照 W3C 专利 政策运作的工作组编制。 W3C 维护着一份 公开的专利 披露列表, 其中列出了与该工作组交付成果有关的披露;该页面还包含 披露专利的说明。任何实际 知悉某项专利,并认为该专利包含 必要权利要求 的个人,都必须依照 W3C 专利政策第 6 节披露该信息。

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

1. 简介

本节为非规范性内容。

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

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

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

注

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

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

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

用于处理通用指针输入的事件与鼠标事件非常相似:pointerdown、pointermove、pointerup、pointerover、pointerout 等。这有助于轻松地将内容 从鼠标事件迁移到指针事件。 除了面向其他形式输入的新属性(例如压力、 接触区域几何形状、倾斜度)之外,指针事件还提供鼠标事件中存在的所有常用属性(包括客户端坐标、 目标元素、按钮状态)。在适用的情况下,作者可以轻松针对指针事件编写代码,以便在不同输入 类型之间共享逻辑,并且只在为了获得最佳体验确有必要时才针对特定输入类型进行 自定义。

虽然指针事件来源于各种输入设备,但并未将其定义为由 某组其他特定于设备的事件生成。虽然出于兼容性考虑这是可行且受到鼓励的,但本规范 并不要求支持其他特定于设备的事件(例如鼠标事件或触摸事件)。用户 代理可以在不支持任何其他设备事件的情况下支持指针事件。为了与 针对鼠标特定事件编写的内容兼容,本规范确实提供了一个可选章节,用于说明如何 根据来自鼠标以外设备的指针输入生成兼容性鼠标事件。

注

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

2. 一致性

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

本文档中的关键词 可以、必须、不得、可选 和 应该 应按照 BCP 14 [RFC2119] [RFC8174] 中所述的含义进行解释,但仅当这些关键词如这里所示 全部以大写形式出现时才如此。

3. 指针事件和接口

3.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

导致该事件的指针的唯一标识符。用户代理可以为主鼠标指针保留通用的 pointerId 值 0 或 1。pointerId 值 -1 必须予以保留,并用于表示由 指向设备以外的事物生成的事件。对于任何其他指针,用户 代理可以自由采用不同的策略和方法来分配 pointerId 值。但是,顶级 浏览上下文(如 [HTML] 中所定义)中的所有 活动指针都必须是唯一的,并且该 标识符不得受到任何其他顶级浏览 上下文的影响(即,一个顶级浏览上下文不能假定,当指针移出该浏览上下文并进入另一个 顶级浏览上下文时,该指针的 pointerId 仍将相同)。

用户代理 可以回收先前活动指针中已经停用的 pointerId 值,或者它可以始终为某个特定指向设备重用相同的 pointerId (例如,在多用户协作应用中唯一标识来自特定用户的特定笔/触控笔 输入)。但是,在后一种情况下,为了尽量减少跨不同页面或 域进行指纹识别和跟踪的可能性,pointerId 必须仅在页面/会话的生命周期内显式关联到该特定指向 设备,并且下一次在新会话中再次使用该特定指向设备时,必须选择一个新的随机 pointerId。

注

pointerId 选择 算法取决于具体实现。作者不能假定这些值除了作为 指针的标识符之外还传达任何特定含义,该标识符与所有 其他活动指针的标识符均不同。例如,用户代理可以简单地按照活动指针 变为活动状态的顺序,从 0 开始为它们分配数字—— 但不保证这些值单调递增。由于是否为 特定指向设备重用相同的 pointerId 由各个实现自行决定,因此强烈建议作者 不要依赖这一点,而应改用 persistentDeviceId。

width

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

height

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

pressure

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

tangentialPressure

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

注
尽管该属性的名称如此,但在实践中,生成 该属性值的硬件控件/传感器未必具有压力感应能力。例如, 在大多数情况下,大多数喷枪/绘画触控笔实现上的指轮 都可以自由设置,而不要求用户持续对 指轮施加压力来防止其返回零位置。
tiltX

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

tiltX 说明图
图 2 正的 tiltX。
tiltY

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

tiltY 说明图
图 3 正的 tiltY。
twist

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

altitudeAngle

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

注
此处为 altitudeAngle 定义的默认值为 π/2, 这表示换能器与表面垂直。 这不同于 触摸事件 - 第 2 级规范中 对 altitudeAngle 属性的定义,后者的默认值为 0。
altitudeAngle 说明图
图 4 altitudeAngle 为 π/4 的示例(相对于 X-Y 平面为 45 度)。
azimuthAngle

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

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

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

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

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

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

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

persistentDeviceId

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

注
由于数字化器和指向设备的硬件限制, 不保证指向设备的所有指针事件都能获得 persistentDeviceId。例如,设备可能未能及时向 数字化器报告其硬件 ID,因此 pointerdown 可能没有 persistentDeviceId。在这种情况下,persistentDeviceId 最初可能 为 0,随后变为有效值。
getCoalescedEvents()

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

getPredictedEvents()

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

PointerEventInit 字典由 PointerEvent 接口的构造函数使用,用于提供 构造不受信任(合成)指针事件的机制。它继承自 MouseEventInit 字典。有关演示如何触发 不受信任指针事件的示例代码,请参阅示例。

对于 PointerEvent 的事件 构造步骤 会将 PointerEventInit 的 coalescedEvents 克隆到合并事件列表,并将 PointerEventInit 的 predictedEvents 克隆到预测事件列表。

注
PointerEvent 接口继承自 MouseEvent。 另请注意 CSSOM 视图模块中提出的扩展,它将 各种坐标属性从 long 更改为 double,以允许使用小数坐标。对于已经 为 PointerEvent 实现这一拟议扩展、但没有为普通 MouseEvent 实现的用户代理,当涉及 click、auxclick 和 contextmenu 事件时,还有其他要求。

3.1.1 按钮状态

3.1.1.1 组合按钮 交互

某些指针设备(例如鼠标或笔)支持多个按钮。在鼠标事件模型中, 每次按下按钮都会产生一个 mousedown 和 mouseup 事件。为了更好地 抽象这种硬件差异并简化跨设备输入的编写,指针事件对于组合按钮按下 不会触发彼此重叠的 pointerdown 和 pointerup 事件 (即指针设备上的另一个按钮已经 按下时,再按下一个附加按钮)。

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

对 button 和 buttons 属性的修改仅适用于 指针事件。然而,对于 click、auxclick 和 contextmenu,button 和 buttons 的值必须遵循鼠标事件模型,与兼容性鼠标事件的情况相同。

3.1.1.2 button 属性

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

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

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

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

3.1.2 主指针

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

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

3.1.3 使用 PointerEvent 接口触发事件

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

如果事件不是 gotpointercapture、lostpointercapture、click、 auxclick 或 contextmenu 事件,则为 此 PointerEvent 运行处理待定指针捕获步骤。

按如下方式确定 事件触发的目标:

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

如果事件是 pointerdown、pointermove 或 pointerup,则将事件的 活动文档 对应于其 pointerId 设置为 targetDocument。

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

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

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

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

注
使用指针捕获 目标覆盖作为目标而不是正常的命中测试结果,可能会触发某些 边界事件。这等同于指针离开其先前目标并进入这个 新的捕获目标。当释放捕获时,可能发生相同的情况,因为指针 会离开捕获目标并进入命中测试目标。
3.1.3.1 属性和默认动作

本规范中定义的事件类型的 bubbles 和 cancelable 属性以及默认动作 如下表所示。有关每种 事件类型的详细信息,请参阅指针事件类型。

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

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

对于 pointerenter 和 pointerleave 事件,composed [DOM] 属性应该为 false;对于上表中的所有其他指针事件,该属性应该为 true。

对于上表中的所有指针事件,detail 属性 应该为 0。

注
许多用户代理在 MouseEvents 中公开非标准属性 fromElement 和 toElement,以支持旧版内容。我们鼓励这些用户 代理将 PointerEvents 中这些(继承的)属性值设置为 null,以促使作者迁移到使用标准化的替代属性 (target 和 relatedTarget)。

与 MouseEvent 的 relatedTarget 类似, relatedTarget 应初始化为指针刚刚离开其边界的元素 (对于 pointerover 或 pointerenter 事件),或者初始化为指针正在进入其边界的元素(对于 pointerout 或 pointerleave)。对于其他指针事件, 此值默认为 null。请注意,当一个元素获得指针捕获后,该指针的所有 后续事件都被视为位于捕获 元素的边界内。

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

3.1.3.2 处理待定指针捕获

当隐式释放指针 捕获时,以及触发不是 gotpointercapture 或 lostpointercapture 的指针事件时,用户代理必须运行以下步骤。

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

如 click、 auxclick 和 contextmenu 事件一节中所定义,即使 lostpointercapture 事件已经 被分派, 相应的 click、auxclick 或 contextmenu 事件(如果有)仍会被分派到捕获目标。

3.1.3.3 抑制指针事件流

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

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

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

  • 指针处于活动状态时设备的屏幕方向发生变化。
  • 用户尝试同时使用比设备 支持数量更多的指针输入进行交互。
  • 用户代理将输入解释为意外输入(例如,硬件 支持手掌拒绝)。

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

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

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

相对于屏幕表面发生移动或其任一 属性发生某种变化的指向设备,会按照指针事件类型中的定义触发各种事件。对于 静止的指向设备(既未相对于屏幕表面移动,也未发生任何 属性变化),如果布局变化影响了该 指针的命中测试目标,用户代理必须 在布局变化之后触发某些边界事件;有关详细信息,请参阅 pointerover、pointerenter、pointerout 和 pointerleave。用户代理可以出于性能 原因延迟触发这些边界事件(例如,避免过多的命中测试或由边界事件监听器导致的布局变化)。

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

3.1.5 在 tiltX / tiltY 与 altitudeAngle / azimuthAngle 之间转换

指针事件包含两组互补的属性,用来表示 换能器相对于 X-Y 平面的方向:tiltX / tiltY(在 最初的 Pointer Events 规范中引入),以及 azimuthAngle / altitudeAngle (采用自 触摸事件 - 第 2 级 规范)。

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

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

示例 1: 在 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};
}

3.2 PointerEvent 算法

3.2.1 初始化一个 PointerEvent

要使用 event、eventType 和 eventTarget、bubbles 以及 cancelable 初始化一个 PointerEvent,运行以下步骤:

  1. 使用 event、eventType 和 eventTarget、bubbles 以及 cancelable 初始化一个 MouseEvent
  2. 将所有其他属性初始化为默认的 PointerEvent 值。

3.2.2 创建一个 PointerEvent

要使用 eventType 和 eventTarget、 bubbles 以及 cancelable 创建 一个 PointerEvent,运行以下步骤:

  1. 令 event 为使用 PointerEvent 创建一个 事件的结果
  2. 使用 event、eventType 和 eventTarget、bubbles 以及 cancelable 初始化一个 PointerEvent
  3. 返回 event

3.2.3 从 MouseEvent 创建 PointerEvent

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

3.3 指针事件类型

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

对于主指针,这些事件(gotpointercapture 和 lostpointercapture 除外)还可能触发兼容性鼠标事件。

3.3.1 pointerover

类型 pointerover
接口 PointerEvent
冒泡 是
可取消 是
组合 是
上下文
(可信事件)

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

3.3.2 pointerenter

类型 pointerenter
接口 PointerEvent
冒泡 否
可取消 否
组合 否
上下文
(可信事件)

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

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

3.3.3 pointerdown

类型 pointerdown
接口 PointerEvent
冒泡 是
可取消 是
组合 是
上下文
(可信事件)

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

注
对于鼠标(或其他多按钮指针设备),这意味着 pointerdown 和 pointerup 并非在所有相同的 情况下都像 mousedown 和 mouseup 那样触发。参见 组合按钮 以了解更多信息。

对于输入 设备,若其不支持 悬停,则 用户 代理 必须 还要 触发指针 事件,名称为 pointerover,随后触发一个 指针事件,其名称为 pointerenter,然后才可 派发 pointerdown 事件。

注
作者可以阻止某些 兼容性鼠标事件 的触发,方法是取消 该 pointerdown 事件(如果 isPrimary 属性为 true)。这会设置 该指针上的 PREVENT MOUSE EVENT 标志。不过,请注意,这不会阻止 mouseover、mouseenter、mouseout 或 mouseleave 事件的触发。

3.3.4 pointermove

类型 pointermove
接口 PointerEvent
冒泡 是
可取消 是
组合 是
上下文
(可信事件)

当指针的任何属性发生变化,但该变化不会触发 pointerdown 或 pointerup 事件时,用户代理必须触发一个指针事件,其名称为 pointermove。这包括 坐标、压力、切向压力、 倾斜、旋转、接触几何形状(width 和 height)或组合按钮的任何变化。

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

3.3.5 pointerrawupdate

类型 pointerrawupdate
接口 PointerEvent
冒泡 是
可取消 否
组合 是
上下文
(可信事件)

当指针的任何属性发生变化,但该变化不会触发 pointerdown 或 pointerup 事件时,用户代理必须触发一个指针事件, 其名称为 pointerrawupdate,并且 只能在安全上下文中这样做。 有关此类属性的列表,请参阅 pointermove 事件。

与 pointermove 相比,用户代理 应该尽快分派 pointerrawupdate 事件, 并且频率应尽可能高,以 JavaScript 能够处理这些事件的速度为限。

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

请注意,如果已经有另一个 pointerrawupdate 具有相同的 pointerId,并且尚未在 事件循环中分派,则 用户代理可以将新的 pointerrawupdate 与该事件合并,而不是 创建新的任务。 这可能导致 pointerrawupdate 包含合并事件,并且一旦该事件在 事件循环中被处理, 它们都会作为一个 pointerrawupdate 事件的合并事件立即交付。 有关更多信息,请参阅getCoalescedEvents。

就 pointerrawupdate 和 pointermove 的顺序而言, 如果用户代理从平台接收到一个同时导致 pointerrawupdate 和 pointermove 事件的更新, 那么用户代理必须在相应的 pointermove 之前分派 pointerrawupdate 事件。

除 target 外,自上一个 pointermove 事件以来所有已分派 pointerrawupdate 事件的合并事件列表连接起来后, 就其他事件属性而言,与下一个 pointermove 事件的合并事件相同。 pointerrawupdate 的属性 大多与 pointermove 相同,唯一的例外是 cancelable,对于 pointerrawupdate,其必须为 false。

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

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

3.3.6 pointerup

类型 pointerup
接口 PointerEvent
冒泡 是
可取消 是
组合 是
上下文
(可信事件)

该 用户代理 必须 触发指针事件,名称为 pointerup,触发条件为指针离开 活动按钮状态。对于鼠标,这是指 设备从至少有一个按钮被按下转换为没有按钮被按下的状态。对于触摸,这是指 解除物理接触,接触对象为 数字化器。对于笔,这是指笔 在没有按钮被按下时解除与数字化器的物理接触,或进行以下转换: 在悬停时,从至少有一个按钮被按下到没有按钮被按下。

对于输入 设备,若其不支持 悬停,则 用户 代理 必须 还要 触发指针 事件,名称为 pointerout,随后触发一个 指针事件,其名称为 pointerleave,这些操作须晚于 派发 pointerup 事件。

所有 pointerup 事件的 pressure 值均为 0。

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

注
对于鼠标(或其他多按钮指针设备),这意味着 pointerdown 和 pointerup 并非在所有相同的 情况下都像 mousedown 和 mouseup 那样触发。参见 组合按钮 以了解更多信息。

3.3.7 pointercancel

类型 pointercancel
接口 PointerEvent
冒泡 是
可取消 否
组合 是
上下文
(可信事件)

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

pointercancel 事件的以下属性值必须与具有相同 pointerId 的最后一个已分派指针事件的值相匹配:width、 height、pressure、tangentialPressure、tiltX、 tiltY、twist、altitudeAngle、azimuthAngle、 pointerType、isPrimary,以及从鼠标事件继承的坐标。 pointercancel 事件中的 coalescedEvents 和 predictedEvents 列表必须为空,并且事件的 cancelable 属性 必须为 false。

3.3.8 pointerout

类型 pointerout
接口 PointerEvent
冒泡 是
可取消 是
组合 是
上下文
(可信事件)

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

3.3.9 pointerleave

类型 pointerleave
接口 PointerEvent
冒泡 否
可取消 否
组合 否
上下文
(可信事件)

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

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

3.3.10 gotpointercapture

类型 gotpointercapture
接口 PointerEvent
冒泡 是
可取消 否
组合 是
上下文
(可信事件)

当一个元素获得指针捕获时,用户代理必须触发一个指针事件,其名称为 gotpointercapture。 此事件在正在获得指针捕获的元素上触发。该指针的后续事件将 在此元素上触发。请参阅设置指针捕获和处理待定指针捕获各节。

3.3.11 lostpointercapture

类型 lostpointercapture
接口 PointerEvent
冒泡 是
可取消 否
组合 是
上下文
(可信事件)

在某个指针的指针捕获被释放后,用户代理必须触发一个指针事件,其名称为 lostpointercapture。 在捕获被释放后,该事件必须先于该指针的任何后续事件触发。 此事件在被移除指针捕获的元素上触发。除 click、auxclick 和 contextmenu 事件之外,该指针的所有后续事件都遵循正常的命中测试机制 (不在本规范范围内)来确定事件目标。请参阅释放 指针捕获、隐式释放 指针捕获以及处理待定指针 捕获各节。

3.3.12 click、auxclick 和 contextmenu

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

这些事件必须是 PointerEvent 类型,并且还要遵守 本节其余部分中提到的附加要求。

3.3.12.1 事件属性

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

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

如 PointerEvent 中所述,CSSOM 视图模块提议将各种坐标属性 (screenX、screenY、pageX、pageY、 clientX、 clientY、x、y、offsetX、 offsetY)重新定义为 double,以允许使用小数坐标。 然而,当此更改仅应用于 PointerEvent,而不应用于普通的 MouseEvent 时,已证明会在 click、auxclick 和 contextmenu 的情况下导致旧代码的 Web 兼容性问题。因此,仅为 PointerEvent 实现CSSOM 视图模块中所提议 更改的用户代理必须 使用 Math.floor [ECMASCRIPT] 将 click、auxclick 和 contextmenu 的各种坐标属性转换为 long 值(如原始鼠标事件中所定义)。

3.3.12.3 事件分派

一个 click、auxclick 或 contextmenu 事件 必须 遵循标准派发流程,但事件目标 使用以下算法覆盖:

  1. 令 event 为 click、auxclick 或 contextmenu 事件中正在派发的那个,并令 userEvent 为用户 交互事件,其导致了 event 的触发。

    注

    事件 userEvent 可以不是 PointerEvent;例如, 它是 KeyboardEvent,此时 click 事件的派发 由在复选框元素上按下空格键引起。

    当 userEvent 是 PointerEvent 时,userEvent 是 一个 pointerup ,对应于 click 或 auxclick 事件;或者是 pointerdown 或 pointerup 事件(取决于原生平台惯例),对应于 contextmenu 事件。

  2. 如果 userEvent 不是 PointerEvent,则派发 event 时不覆盖 event 的目标,并跳过下面的剩余步骤。
  3. 按如下方式定义 target:

    如果 event 是 contextmenu 事件,或 userEvent 是在 对应指针被捕获时派发的,则令 target 为 userEvent 的目标。

    否则(event 是 click 或 auxclick 事件, 其 userEvent 是一个 pointerup 事件,该事件派发时 未被捕获),令 target 为以下目标的最近公共含自身祖先: DOM 中对应的 pointerdown 和 pointerup 目标,以 event 正在派发的时刻为准。

  4. 将 event 派发到 target。

    注
    如果 userEvent 被捕获,则 event 会被派发到 userEvent 的捕获目标,尽管 lostpointercapture 事件 具有相同的 pointerId 且已被 派发。

4. 对 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。

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

5. 对 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 属性。

6. 对 Navigator 接口的扩展

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

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

获取器步骤如下:

  1. 令 emulated maxTouchPoints 为 WebDriver BiDi 模拟的最大触摸点数 [WEBDRIVER-BIDI] 的结果。
  2. 如果 emulated maxTouchPoints 不为 null,则返回 emulated maxTouchPoints。
  3. 返回设备支持的最大同时触摸接触点数量。 对于具有多个数字化器的设备(例如 多个触摸屏),该值必须是每个 单独数字化器所支持的最大接触点数量集合中的最大值。

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

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

7. 声明直接操纵行为

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

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

7.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 值一节。

在即将开始平移或缩放之前,如果以下所有条件都为真,用户代理必须抑制指针事件流:

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

7.2 确定受支持的直接操纵行为

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

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

7.3 touch-action 值的详细信息

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

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

指定方向的平移值有助于自定义某些越界滚动行为。 例如,要实现简单的下拉刷新效果,文档的 touch-action 可以设置为 pan-x pan-down,条件是滚动位置 为 0,否则设置为 pan-x pan-y。 这允许指针事件处理程序定义向上平移/滚动的行为,条件是这些操作 从文档顶部开始。

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

注
禁用某些默认的直接操纵平移和缩放行为,可能使 用户代理更快地响应其他行为。例如,使用 auto 时,用户 代理通常会增加 300ms 的延迟,在 click 之前,以便双击手势能够被 处理。在这些情况下,显式设置 touch-action: none 或 touch-action: manipulation 将消除这段延迟。请注意,用于 判定单击或双击手势的方法不在本规范的范围内。
示例 2:禁止 所有直接操纵行为
<div style="touch-action: none;">
    此元素会接收所有原本会导致平移或缩放的直接操纵交互的指针事件。
</div>
示例 3:仅允许 水平平移
<div style="touch-action: pan-x;">
    当未沿水平方向平移时,此元素会接收指针事件。
</div>
示例 4:禁止 用于平移和缩放的直接操纵行为的子区域
<div style="overflow: auto;">
    <div style="touch-action: none;">
        此元素会接收所有原本会导致平移或缩放的直接操纵交互的指针事件。
    </div>
    <div>
        此元素上的直接操纵交互可以被用于操纵父元素。
    </div>
</div>
示例 5: 禁止用于平移和缩放的直接操纵行为的中间父元素
<div style="overflow: auto;">
    <div style="touch-action: pan-y;">
        <div style="touch-action: pan-x;">
            此元素会接收所有直接操纵交互的指针事件,因为
            它仅允许水平平移,而某个中间祖先
            (位于它与可滚动元素之间)仅允许垂直平移。
            因此,用户代理不会处理任何用于平移/缩放的直接操纵
            行为。
        </div>
    </div>
</div>
示例 6: 限制允许的用于平移和 缩放的直接操纵行为的中间父元素
<div style="overflow: auto;">
    <div style="touch-action: pan-y pan-left;">
        <div style="touch-action: pan-x;">
            当未向左平移时,此元素会接收指针事件。
        </div>
    </div>
</div>

8. 指针 捕获

8.1 简介

本节为非规范性内容。

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

自定义音量滑块
图 6 一个自定义滑块控件示例,通过来回滑动滑块块 元素来选择值。在滑块块上发生 pointerdown 后,可以使用指针捕获, 使用户即使指针偏离滑块块也仍能滑动它。

8.2 设置指针捕获

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

  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 监听器中。

8.3 释放指针捕获

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

  1. 如果作为方法参数提供的 pointerId 与任何活动指针都不匹配,并且这些步骤 不是由于隐式释放指针 捕获而调用的,则抛出一个 "NotFoundError" DOMException。
  2. 如果对于具有指定 pointerId 的 Element,hasPointerCapture 为 false,则终止这些 步骤。
  3. 对于指定的 pointerId,如果设置了待定指针捕获目标 覆盖,则将其清除。
注
参见设置指针捕获一节中的注释。

8.4 隐式指针捕获

输入若实现 直接操纵 交互以进行平移和缩放 (例如触摸屏上的触摸或触控笔),应该 表现得完全如同 setPointerCapture 被调用于 目标元素,调用时机恰在调用任何 pointerdown 监听器之前。该 hasPointerCapture API 可用于(举 例来说,在 pointerdown 监听器中)判断这是否已 发生。如果 releasePointerCapture 未在 下一个指针事件触发前针对该指针调用,则一个 gotpointercapture 事件将被派发到 目标(照常),表明捕获处于活动状态。

注
这是一次不兼容变更,相对于 [PointerEvents] 而言,但不会 影响绝大多数现有内容。除了符合典型平台的用户体验 惯例外,这种隐式捕获设计还使用户代理能够进行性能优化, 从而无需对触摸移动事件执行命中测试,且不要求开发者显式 选择启用(与现有主流原生和 Web API 的性能特性一致,这些 API 用于 触摸输入)。
注
此外,用户代理可以实现隐式指针捕获行为,适用于所有输入 设备,作用于范围输入控件等特定 UI 部件(允许手指的部分移动 在交互过程中超出表单控件本身的范围)。

8.5 隐式释放指针捕获

紧接在触发 pointerup 或 pointercancel 事件之后, 该 用户代理 必须 清除 待处理的指针捕获目标 覆盖值 ,其对应的 pointerId 属于 pointerup 或 pointercancel 事件,该事件刚刚派发, 然后运行 处理待处理的指针捕获 步骤以触发 lostpointercapture(如有必要)。 运行 处理待处理的指针捕获 步骤之后, 如果指针支持悬停,用户代理 必须 还 发送所需的相应边界事件, 以反映指针在未被捕获时的当前位置。

当 指针捕获目标覆盖值 不再处于 已连接 [DOM] 状态时, 该 指针捕获目标覆盖值 应该 被设置为文档。

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

注
前面两段会导致一个 lostpointercapture 事件 与被捕获的指针对应,并在文档上触发, 时机是在下一次 处理待处理的指针 捕获 期间,该次处理发生在捕获节点被移除之后。

当指针锁定 [PointerLock] 成功应用于元素时,该 用户代理 必须 运行步骤,如同 releasePointerCapture 方法已 被调用一样,前提是存在被设置为已捕获或待捕获状态的元素。

9. 合并事件和预测事件

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

9.1 合并事件

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

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

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

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

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

示例 7:使用合并事件列表的基本 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 的属性将以最能表示 合并事件列表中事件的方式进行初始化。用户代理具体应通过什么方法实现这一点, 不在 本规范范围内。

所有这些已分派事件的顺序必须与原始事件的实际顺序 相匹配。 例如,如果一个 pointerdown 事件导致 合并的 pointermove 事件被分派,则用户代理必须首先 分派一个 pointermove 事件,其中包含某个 pointerId 的所有这些合并事件,然后 再分派 pointerdown 事件。

注

下面是实际事件随 timeStamp 值递增而发生, 以及 用户代理所分派事件的一个示例:

实际事件 已分派事件
指针(pointerId=2) 坐标变化 pointerrawupdate(pointerId=2),包含一个 合并事件
指针(pointerId=1) 坐标变化 pointerrawupdate(pointerId=1),包含一个 合并事件
指针(pointerId=2) 坐标变化 pointerrawupdate(pointerId=2),包含一个 合并事件
指针(pointerId=2) 坐标变化 pointerrawupdate(pointerId=2),包含一个 合并事件
指针(pointerId=1) 坐标变化 pointerrawupdate(pointerId=1),包含一个 合并事件
指针(pointerId=2) 坐标变化 pointerrawupdate(pointerId=2),包含一个 合并事件
指针(pointerId=1)按钮 按下 pointermove (pointerId=1),包含两个 合并事件
pointermove (pointerId=2),包含四个 合并事件
pointerdown (pointerId=1),包含零个 合并事件
指针(pointerId=2) 坐标变化 pointerrawupdate(pointerId=2),包含一个 合并事件
指针(pointerId=2) 坐标变化 pointerrawupdate(pointerId=2),包含一个 合并事件
指针(pointerId=1)按钮 释放 pointermove (pointerId=2),包含两个 合并事件
pointerup(pointerId=1),包含零个 合并事件

9.2 预测事件

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

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

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

注

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

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

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

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

注

请注意,作者只应将预测事件视为有效预测,直到下一个 指针事件被 分派为止。根据用户代理预测事件所延伸到未来的程度, 普通 指针事件可能会早于一个或多个预测事件的时间戳被分派。

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

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

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

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

  1. 将事件的 pointerId、 pointerType、 isPrimary 和 isTrusted 设置为与 “父”指针事件的相应属性相匹配。
  2. 将事件的 cancelable 和 bubbles 设置为 false(因为 这些事件永远不会 被单独分派)。
  3. 将事件的合并事件列表和预测事件列表设置为空列表。
  4. 将所有其他属性初始化为默认的 PointerEvent 值。

当可信的 PointerEvent 的 target 被更改时,对于 合并事件列表和预测事件列表中的每个事件,用户代理 应该:

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

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

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

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

注

总体而言,兼容性鼠标事件旨在 "交错" 于其各自对应的 指针事件之间。然而,这一特定顺序并非强制,用户代理若实现 兼容性鼠标事件,则 可以 决定延迟或分组派发 鼠标事件,只要它们的相对顺序保持一致。

特别是在触摸屏输入的情况下,用户代理 可以 应用 额外的手势识别启发式方法(除非作者显式加以抑制,所用方式是 touch-action)。在一段序列中, 其中的事件位于一个 pointerdown 事件与一个 pointerup 事件之间,手势识别可能需要 等到 pointerup 事件才能检测或 忽略手势。因此,整个序列的兼容性鼠标事件可能 被一起派发,时间是在最后一个 pointerup 事件之后,如果用户 代理判定该交互并非意在执行某种特定手势。这些有关 用户代理手势识别的细节在本规范中未作定义,可能因 实现而异。

无论是否支持兼容性鼠标事件,用户代理 必须 始终支持 click、auxclick 和 contextmenu 事件,因为 这些事件的类型为 PointerEvent,因此并非 兼容性鼠标事件。调用 preventDefault 的操作若发生在指针事件期间,不得 影响 click、auxclick 或 contextmenu 是否触发。

注

某些高层事件的相对顺序(例如 contextmenu、focus、blur),相对于指针 事件,尚未定义且因用户代理而异。例如,在某些用户代理中, contextmenu 通常会跟在一个 pointerup 之后,而在其他用户代理中,它通常出现在一个 pointerup 或 pointercancel 之前,在某些情况下,它可能 在没有任何对应指针事件的情况下触发(例如,由键盘 交互引起)。

此外,用户代理可以应用自己的启发式方法,以决定是否应触发一个 click、auxclick 或 contextmenu 事件。有些 用户代理可能选择不触发这些事件,如果存在其他(非主)指针,类型与其 相同,或存在不同类型的其他主指针。用户代理可能认定某个 特定操作不是一次 "纯粹的" 点按、点击或长按(例如,如果一次交互 由手指在触摸屏上进行,且包含过多移动,而手指仍接触 屏幕),并决定不触发 click、auxclick 或 contextmenu 事件。用户代理行为的这些方面未在本 规范中定义,可能因实现而异。

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

作者可以阻止某些兼容性鼠标事件的生成,方法是取消 pointerdown 事件。

鼠标事件只能在指针按下时被阻止。悬停的指针(例如,没有按钮 被按下的鼠标)的鼠标事件无法被阻止。

这些 mouseover、mouseout、mouseenter 和 mouseleave 事件永远不会被阻止(即使指针已按下)。

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

10.1 跟踪旧式鼠标 指针的有效位置

虽然只有 主指针 能产生兼容性鼠标事件,但 多个主指针 可以同时处于活动状态,每个 都产生自己的兼容性鼠标事件。为了与依赖 MouseEvents 的脚本兼容, 鼠标过渡事件(mouseover、mouseout、mouseenter 和 mouseleave)应该 模拟 单个 传统鼠标输入的移动。这意味着每个事件目标的进入/离开状态都是有效的。用户代理 应该 保证这一点,方法是维护 传统鼠标指针的有效位置,在文档中的维护方式如下。

就在触发一个 pointerdown、pointerup 或 pointermove 事件,或一个 pointerleave 事件(目标为 window)之前, 用户代理 应该 执行以下步骤:

  1. 令 T 为以下事件的目标:pointerdown、pointerup 或 pointermove 事件,该事件正在被派发。对于 pointerleave 事件,将 T 设为未设置状态。
  2. 如果 T 与当前的 传统鼠标指针的有效 位置 均未设置或两者相等,则终止这些步骤。
  3. 派发 mouseover、mouseout、mouseenter 和 mouseleave 事件,按照鼠标事件的规定处理鼠标从当前 传统鼠标指针的有效 位置 到 T 的移动。将未设置的值(无论是当前的 传统鼠标指针的有效 位置 还是 T)视为窗口之外的鼠标位置。
  4. 将 传统鼠标指针的有效 位置 设置为 T。
注

旧式鼠标指针的有效位置模拟了这样一个事实:我们并非总能将 指针转换事件(pointerover、pointerout、 pointerenter 和 pointerleave)直接映射到相应的旧式鼠标转换事件 (mouseover、 mouseout、mouseenter 和 mouseleave)。下面的 动画展示了一种情况: 用户代理需要分派比指针转换事件更多的旧式鼠标转换事件,才能使用 单个旧式鼠标输入协调两个主指针。

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

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

10.2 支持悬停的设备 的映射

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

  1. 如果待派发的指针事件的 isPrimary 属性为 false 则派发该指针事件并终止这些步骤。
  2. 如果待派发的指针事件是一个 pointerdown、pointerup 或 pointermove 事件,或一个 pointerleave 事件(目标为 window), 则派发兼容性鼠标过渡事件,具体见 跟踪 传统鼠标指针的有效位置。
  3. 派发该指针事件。
  4. 如果已派发的指针事件为 pointerdown 且该事件的 已取消标志 已设置,则设置 PREVENT MOUSE EVENT 标志(对应此 pointerType)。
  5. 如果 PREVENT MOUSE EVENT 标志 未 设置于此 pointerType,且已派发的指针事件为:
  6. 如果已派发的指针事件为 pointerup 或 pointercancel,则清除 PREVENT MOUSE EVENT 标志(对应此 pointerType)。

10.3 不支持悬停的 设备的映射

某些设备(例如大多数触摸屏)在不处于活动状态时,不支持在某个坐标(或一组坐标)上 悬停。大量现有针对鼠标事件编写的内容都假定这些事件由鼠标 产生,因此通常认为以下特性成立:

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

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

  1. 如果待派发的指针事件的 isPrimary 属性为 false 则派发该指针事件并终止这些步骤。
  2. 如果待派发的指针事件为 pointerover 且 pointerdown 事件尚未派发 (针对此指针),则触发一个 mousemove 事件(为了兼容传统的 鼠标专用代码)。
  3. 如果待派发的指针事件是一个 pointerdown、pointerup 或 pointermove 事件,或一个 pointerleave 事件(目标为 window), 则派发兼容性鼠标过渡事件,具体见 跟踪 传统鼠标指针的有效位置。
  4. 派发该指针事件。
  5. 如果已派发的指针事件为 pointerdown 且该事件的 已取消标志 已设置,则设置 PREVENT MOUSE EVENT 标志(对应此 pointerType)。
  6. 如果 PREVENT MOUSE EVENT 标志 未 设置于此 pointerType,且已派发的指针事件为:
  7. 如果已派发的指针事件为 pointerup 或 pointercancel,则清除 PREVENT MOUSE EVENT 标志(对应此 pointerType)。

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

注

激活元素(click)所用的主指针若 不支持悬停(例如, 触摸屏上的单根手指),通常会产生以下事件序列:

  1. mousemove
  2. pointerover
  3. pointerenter
  4. mouseover
  5. mouseenter
  6. pointerdown
  7. mousedown
  8. 零个或多个 pointermove 和 mousemove 事件,具体取决于指针的移动
  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

11. 鼠标事件和接口

鼠标事件模块源自 [HTML401] 的 onclick、 ondblclick、onmousedown、onmouseup、onmouseover、 onmousemove 和 onmouseout 属性。此事件模块专门设计 用于指点输入设备,例如鼠标或轨迹球。

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

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

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

注

目标元素的祖先可以利用事件冒泡获得鼠标事件的通知, 这些事件发生在其后代元素内。

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

注

在初始化 MouseEvent 对象时,若使用 initMouseEvent,实现可以使用客户区坐标 clientX 和 clientY 来计算其他坐标 (例如 DOM 第 0 级实现所暴露的目标坐标或其他专有属性, 如 pageX)。

11.1 MouseEvent 接口

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

[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

事件发生位置的水平坐标,相对于原点(屏幕 坐标系)。

此 未初始化值 对于该 属性 必须 为 0。

screenY

事件发生位置的垂直坐标,相对于原点(屏幕 坐标系)。

此 未初始化值 对于该 属性 必须 为 0。

clientX

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

此 未初始化值 对于该 属性 必须 为 0。

clientY

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

此 未初始化值 对于该 属性 必须 为 0。

layerX

水平偏移量,以最近的满足以下条件的 祖先 元素为参照:该元素是一个 层叠上下文、已 定位,或在以下过程的 定位阶段被绘制:绘制层叠 上下文。

此 未初始化值 对于该 属性 必须 为 0。

layerY

垂直偏移量,以最近的满足以下条件的 祖先 元素为参照:该元素是一个 层叠上下文、已 定位,或在以下过程的 定位阶段被绘制:绘制层叠 上下文。

此 未初始化值 对于该 属性 必须 为 0。

ctrlKey

参见 KeyboardEvent 的 ctrlKey 属性。

此 未初始化值 对于该 属性 必须 为 false。

shiftKey

参见 KeyboardEvent 的 shiftKey 属性。

此 未初始化值 对于该 属性 必须 为 false。

altKey

参见 KeyboardEvent 的 altKey 属性。

此 未初始化值 对于该 属性 必须 为 false。

metaKey

参见 KeyboardEvent 的 metaKey 属性。

此 未初始化值 对于该 属性 必须 为 false。

button

在按下或释放鼠标按钮所引发的鼠标事件期间,button 必须 用于指示指点设备的哪个按钮改变了状态。

取值对于 button 属性而言 必须 如下:

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

某些指点设备提供或模拟更多按钮状态,而大于 2 或小于 0 的值 可以 用于表示 此类按钮。

注

取值对于 button 而言,不会 因并非由鼠标按钮按下/释放所引发的事件而更新。在这些 情形中,请注意不要将值 0 解释为左键,而 应将其解释为默认值。

注

某些 默认动作 关联于 某些事件,例如 mousedown 和 mouseup,并取决于具体哪个鼠标按钮 正在被使用。

此 未初始化值 对于该 属性 必须 为 0。

buttons

在任何鼠标事件期间,buttons 必须 用于指示哪些鼠标按钮的组合 当前正被按下,以位掩码表示。

注

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

注

尽管名称相似,但以下两个属性的值,即 buttons 属性和 button 属性的值,有很大 差异。以下属性的值,即 button 的值, 假定在以下情况下有效:mousedown / mouseup 事件处理器运行期间,而 buttons 属性反映了 鼠标按钮的状态,适用于任何受信任的 MouseEvent 对象(在其正在 派发时),因为它可以表示 "当前没有活动按钮" 状态(0)。

取值对于 buttons 属性而言 必须 如下:

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

某些指点设备提供或模拟更多按钮。为了表示此类按钮,其值 必须 随每个后续按钮依次加倍(按二进制序列 8、16、32、... )。

注

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

注

某些 默认动作 关联于 某些事件,例如 mousedown 和 mouseup,并取决于具体哪个鼠标按钮 正在被使用。

此 未初始化值 对于该 属性 必须 为 0。

relatedTarget

用于标识一个次要的 EventTarget,与 UI 事件相关,具体取决于事件类型。

此 未初始化值 对于该 属性 必须 为 null。

此 relatedTarget 应该 初始化为鼠标指针刚刚离开其边界的元素(在 mouseover 或 mouseenter 事件的情况下),或另一个元素,其边界 正被鼠标指针进入(在 mouseout 或 mouseleave 或 focusout 事件的情况下)。对于其他事件,此 值无需赋值(默认为 null)。

getModifierState(keyArg)

使用键值查询修饰键的状态。

如果它是修饰键且该修饰键已激活,则返回 true, 否则返回 false。

DOMString keyArg
参见 KeyboardEvent 的 getModifierState() 方法中对此参数的描述。

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

11.2 MouseEvent 算法

11.2.1 原生操作系统要求

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

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

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

11.2.2 构造鼠标事件

警告

本节需要修订。

一般而言,当某个构造函数属于 Event 接口,或属于某个接口, 该接口继承自 Event 接口,且该构造函数被调用时, 步骤(描述于 [DOM])应该被遵循。然而,MouseEvent 接口提供额外的 字典成员,用于初始化内部状态,该状态属于 Event 对象的修饰 键:具体而言,就是通过以下方法查询的内部状态:getModifierState() 方法。本节补充 [DOM] 中的步骤,以初始化一个新的 MouseEvent 对象,并为其设置这些可选的修饰键 状态。

为构造一个 MouseEvent,或从这些对象派生的对象, 使用下面的算法时,所有 MouseEvent 以及派生 对象都具有 内部修饰键状态,可以通过以下名称设置和读取: 修饰键名称,这些名称描述于 修饰键表,位于 [UIEvents-Key]。

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

  • 如果正在构造的 Event 是一个 MouseEvent 对象或一个派生对象, 派生自该对象,且一个 EventModifierInit 参数已提供给构造函数,则执行以下子步骤:

11.2.3 MouseEvent 的全局状态

警告

本节需要修订。

11.2.3.1 用户代理级 状态

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

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

11.2.3.2 窗口级状态

用户代理必须维护以下由 Window 共享的值。

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

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

11.2.4 MouseEvent 的内部状态

警告

本节需要修订。

MouseEvent 具有以下内部标志,用于 跟踪各种修饰键的状态: shift 标志、 control 标志、 alt 标志、 altgraph 标志, 以及 meta 标志。 如果鼠标事件发生时按下了相应的修饰键,则设置这些标志。

11.2.5 命中测试

警告

本节需要修订。

  1. 令 pos 为相对于视口的 x、y 坐标
  2. 使用 pos 返回 [CSSOM-View] 的 elementFromPoint() (位于 pos 的最前端 DOM 元素)
    注

    为了考虑 惰性或已禁用 元素,应调用 elementsFromPoint() 并排除无效元素。

11.2.6 初始化一个 MouseEvent

警告

本节需要修订。

要使用 event、 eventType 和 eventTarget、bubbles 以及 cancelable 初始化一个 MouseEvent,运行 以下步骤:

  1. 使用 event、eventType、eventTarget、 bubbles 和 cancelable 初始化一个 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 提供一个钩子,而不是在此处对其进行硬编码。

11.2.7 设置鼠标事件修饰键

警告

本节需要修订。

  1. 令 event 为要更新的 MouseEvent
  2. 如果按键修饰键状态包含 "Shift",则设置 event 的shift 标志,否则取消设置
  3. 如果按键修饰键状态包含 "Control",则设置 event 的control 标志,否则取消设置
  4. 如果按键修饰键状态包含 "Alt",则设置 event 的alt 标志,否则取消设置
  5. 如果按键修饰键状态包含 "AltGraph",则设置 event 的altgraph 标志,否则取消设置
  6. 如果按键修饰键状态包含 "Meta",则设置 event 的meta 标志,否则取消设置
  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

11.2.8 创建一个可取消的 MouseEvent

警告

本节需要修订。

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

11.2.9 创建一个不可取消的 MouseEvent

警告

本节需要修订。

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

11.2.10 计算 MouseEvent button 属性

警告

本节需要修订。

这将返回一个适合存储在 MouseEvent 的 button 属性中的按钮 ID。

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

11.2.11 从原生事件设置 MouseEvent 属性

警告

本节需要修订。

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

    待办。

  3. 如果 event.type 是 [ mousedown, mouseup ] 之一,则
    1. 令 mbutton 为来自 native 的、标识按下了哪个鼠标 按钮的 ID
    2. 使用 mbutton 计算 MouseEvent button 属性,并将结果设置为 event.button

11.2.12 处理原生鼠标按下

警告

本节需要修订。

  1. 令 native 为原生 mousedown
  2. 令 mbutton 为来自 native 的、标识按下了哪个鼠标按钮的 ID
  3. 按如下方式更新鼠标按钮位掩码:
    1. 如果 mbutton 是鼠标主按钮,则设置 0x01 位
    2. 如果 mbutton 是鼠标次要按钮,则设置 0x02 位
    3. 如果 mbutton 是辅助(中间)鼠标按钮,则设置 0x04 位
      注

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

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

11.2.13 处理原生鼠标释放

警告

この節は改訂する必要があります。

  1. native をネイティブの mouseup とする
    注記

    mousedown と mouseup の間に他のマウスイベントが発生することがあります。

  2. mbutton を、どのマウスボタンが 押されたかを識別する native の ID とする
  3. マウスボタンビットマスクを次のように更新する:
    1. mbutton がプライマリマウスボタンである場合、0x01 ビットをクリアする
    2. mbutton がセカンダリマウスボタンである場合、0x02 ビットをクリアする
    3. mbutton が補助(中央)マウスボタンである場合、0x04 ビットをクリアする
  4. target を、native からのビューポート相対座標を用いたヒットテストの結果と する
  5. event を、"mouseup"、target を用いてキャンセル可能な MouseEvent を作成する処理の結果と する
  6. native を用いてネイティブから MouseEvent 属性を設定 する
  7. event を用いて、場合によっては pointerup イベントを送信する
  8. target において event を配送 する

11.2.14 处理原生鼠标点击

警告

本节需要修订。

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

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

  2. 令 target 为使用来自 命中测试的视口相对坐标所得的结果 native
  3. 使用 native 和发送 click 事件,并传入 target。

11.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 为创建 PointerEvent 并传入 eventType 和 target 的结果
  7. 如果 native 有效,则
    1. 使用 event、native 从原生事件设置 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 = 当前获得焦点的元素。

11.2.16 处理原生鼠标双击

警告

本节需要修订。

  1. 令 native 为原生鼠标双击事件
    注

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

  2. 令 mbutton 为来自 native 的一个 ID,用于标识按下的是哪个鼠标按钮
  3. 如果 mbutton 不是主鼠标按钮,则返回
  4. 令 target 为使用来自 命中测试的视口相对坐标所得的结果 native
  5. 令 event 为创建 PointerEvent 并传入 "dblclick" 和 target 的结果
  6. 从原生事件设置 MouseEvent 属性,并传入 event、native
  7. 如果 event.screenX 不是整数值,则 将其舍入。
  8. 如果 event.screenY 不是整数值,则 将其舍入。
  9. 在 target 上分派 event

11.2.17 处理原生鼠标移动

警告

本节需要修订。

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

    此算法对 PointerEvents 的分派作出了一些假设,因为目前尚未 明确规定其分派方式。在 pointerevents/285 得到解决后,可能需要更新此算法。

  2. 令 target 为使用来自 命中测试的视口相对坐标所得的结果 native
  3. 令 targetDomPath 为 包含自身的祖先 (针对 target)
  4. 为离开当前元素生成事件:
    1. 如果上一个鼠标元素已定义且不 等于 target,则
      1. 令 mouseout 为创建可取消的 MouseEvent 并传入 "mouseout" 和上一个鼠标元素的结果
        编者注

        TODO:从 native 设置 mouseout 的属性,以及 CSSOM 属性。

    2. 可以使用 mouseout 发送 pointerout 事件
    3. 在 target 上分派 mouseout
      注

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

    4. 令 leaveElements 为上一个 鼠标 DOM 路径的副本,并移除与 targetDomPath 共有的所有元素。
    5. 对于 leaveElements 中的每个 element,执行
      编者注

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

      1. 令 mouseleave 为创建 不可取消的 MouseEvent 并传入 "mouseleave" 和 element 的结果
      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 为创建可取消的 MouseEvent 并传入 "mouseover" 和 target 的结果
        编者注

        TODO:从 native 设置 mouseout 的属性,以及 CSSOM 属性。

      2. 可以使用 mouseover 发送 pointerover 事件
      3. 在 target 上分派 mouseout
        注

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

      4. 令 enterElements 为 targetDomPath 的副本,并移除所有 与上一个鼠标 DOM 路径共有的元素。
      5. 对于 enterElements 中的每个 element,执行
        注

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

        1. 令 mouseenter 为创建 不可取消的 MouseEvent 并传入 "mouseenter" 和 element 的结果
        2. 将 mouseenter.Event.composed 设置为 false
          注

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

        3. 可以使用 mouseenter 发送 pointerenter 事件
          注

          检查影子 DOM 元素的兼容性。Chrome/Linux 会在该 元素和影子根上触发此事件。

        4. 令 result 为在 element 上分派 mouseenter 的结果
      6. 将上一个鼠标元素设置为 target
      7. 将上一个鼠标 DOM 路径设置为 targetDomPath
  6. 令 mousemove 为创建可取消的 MouseEvent 并传入 "mousemove" 和 element 的结果
  7. 设置 mousemove 的 PointerLock 属性
  8. 可以使用 mousemove 发送 pointermove 事件
  9. 在 element 上分派 mousemove

11.2.18 可能显示上下文菜单

警告

本节需要修订。

  1. 令 native 为原生 mousedown 或指针事件
  2. 令 target 为事件的 EventTarget
    1. 令 menuevent 为创建 PointerEvent 并传入 "contextmenu"、target 的结果
    2. 如果 native 有效,则
      1. 从原生事件设置 MouseEvent 属性,并传入 native
    3. 令 result 为在 target 上分派 menuevent 的结果
    4. 如果 result 为 true,则显示用户代理的上下文菜单
注

要处理由键盘触发的上下文菜单,请调用此算法,并将 native = null,将 target = 当前获得焦点的元素。

11.3 鼠标事件顺序

本规范中定义的某些鼠标事件彼此之间必须按照既定顺序 发生。下面显示了当指向设备的光标移动到某个元素上时必须发生的 事件序列:

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

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

事件类型 元素 注释
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
图 10 三个彼此堆叠在一起的 元素的图形表示,指向设备正在堆叠区域上方移动。

当指向设备从元素堆叠区域外部移入标记为 C 的元素,然后再次 移出时,必须发生以下事件序列:

事件类型 元素 注释
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
注

在 mousedown 和 mouseup 事件之间,在仍然触发 click 或 dblclick 事件的情况下,允许的延迟时间、程度、距离以及 mousemove 事件数量将取决于具体实现、设备和平台。 这种容差可以帮助存在身体障碍(例如手部不稳)的用户与指向设备进行交互。

每个实现都会确定适当的滞后 容差,但通常,当相关 mousedown 和 mouseup 事件的目标是同一元素,并且中间没有发生 mouseout 或 mouseleave 事件时,应该触发 click 和 dblclick 事件;而当相关 mousedown 和 mouseup 事件的目标 不同时,应该在最近的公共包含祖先上触发 click 和 dblclick 事件。

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

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

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

11.4 鼠标事件类型

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

11.4.1 auxclick

类型 auxclick
接口 PointerEvent
冒泡 是
可取消 是
组合 是
上下文
(可信事件)

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

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

auxclick 事件可以由同一元素上的 mousedown 和 mouseup 事件先行发生,而忽略 其他节点类型之间的变化(例如文本节点)。根据 环境配置,如果指向设备按钮按下与 释放之间发生一种或多种 mouseover、 mousemove 和 mouseout 事件类型,则可以分派 auxclick 事件。

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

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

对于右键,auxclick 事件会在 任何 contextmenu 事件之后分派。请注意,某些用户 代理会在显示上下文菜单期间吞掉所有输入 事件,因此在此类场景中,应用程序可能无法获得 auxclick。 有关更多说明,请参阅示例 10。

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

11.4.2 click

类型 click
接口 PointerEvent
冒泡 是
可取消 是
组合 是
上下文
(可信事件)

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

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

click 事件可以由同一元素上的 mousedown 和 mouseup 事件先行发生,而忽略 其他节点类型之间的变化(例如文本节点)。根据 环境配置,如果指向设备按钮按下与 释放之间发生一种或多种 mouseover、 mousemove 和 mouseout 事件类型,则可以分派 click 事件。click 事件之后还可以 跟随 dblclick 事件。

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

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

注

为获得最大的可访问性,鼓励内容作者在为自定义 控件定义激活行为时使用 click 事件类型,而不是 mousedown 或 mouseup 等更依赖具体设备的指向设备事件类型。 尽管 click 事件类型起源于指针 设备(例如鼠标),后续实现增强已将其扩展到这种关联之外, 因而可以将其视为一种用于元素激活的 与设备无关的事件类型。

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

11.4.3 contextmenu

类型 contextmenu
接口 PointerEvent
冒泡 是
可取消 是
组合 是
上下文
(可信事件)

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

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

注

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

11.4.4 dblclick

类型 dblclick
接口 MouseEvent
冒泡 是
可取消 是
组合 是
上下文
(可信事件)

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

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

注

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

与 click 事件类型一样,dblclick 事件类型的默认动作会根据事件的目标以及 button 或 buttons 属性的值而有所不同。dblclick 事件类型的典型 默认动作与 click 事件类型的默认动作相同。

11.4.5 mousedown

类型 mousedown
接口 MouseEvent
冒泡 是
可取消 是
组合 是
上下文
(可信事件)

当指向设备 按钮在某个元素上被按下时,用户代理必须分派此事件。

注

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

11.4.6 mouseenter

类型 mouseenter
接口 MouseEvent
冒泡 否
可取消 否
组合 否
上下文
(可信事件)

当指向设备 移动到某个元素或其后代 元素之一的边界上时,用户代理必须分派此事件。当该 元素或其后代之一移动到主 指向设备下方时,用户代理 也必须分派此事件。此事件类型与 mouseover 类似,但 区别在于它不会冒泡,并且当指针设备从一个元素移动到其某个 后代元素的边界上时,不得分派此事件。

注

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

11.4.7 mouseleave

类型 mouseleave
接口 MouseEvent
冒泡 否
可取消 否
组合 否
上下文
(可信事件)

当指向设备 移出某个元素及其所有 后代元素的边界时,用户 代理必须分派此事件。当 该元素或其后代之一移动到不再位于 主指向设备下方时,用户 代理也必须分派此事件。此事件类型与 mouseout 类似, 但区别在于它不会冒泡,并且在指向设备离开 该元素及其所有子元素的边界之前,不得 分派此事件。

注

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

11.4.8 mousemove

类型 mousemove
接口 MouseEvent
冒泡 是
可取消 是
组合 是
上下文
(可信事件)

当指向设备 位于某个元素上方并发生移动时,用户代理必须分派此事件。指向设备移动时事件的 频率取决于具体实现、设备和 平台,但对于持续的指针设备移动,应该触发多个连续的 mousemove 事件, 而不是针对每次鼠标移动仅触发 单个事件。鼓励实现确定最佳频率, 以在响应性与性能之间取得平衡。

注

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

注

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

11.4.9 mouseout

类型 mouseout
接口 MouseEvent
冒泡 是
可取消 是
组合 是
上下文
(可信事件)

当指向设备 移出某个元素的边界,或者该元素 移动到不再位于主指向设备下方时,用户代理必须分派此事件。 此事件类型与 mouseleave 类似,但区别在于 它会冒泡,并且当指针设备 从一个元素移动到其某个后代元素的边界上时,必须分派此事件。

注

另请参阅 mouseover 事件类型。

11.4.10 mouseover

类型 mouseover
接口 MouseEvent
冒泡 是
可取消 是
组合 是
上下文
(可信事件)

当指向设备 移动到某个元素的边界上,或者该元素 移动到主指向设备下方时,用户代理必须分派此事件。 此事件类型与 mouseenter 类似,但区别在于 它会冒泡,并且当指针设备移动到 某个元素的边界上,而该元素的祖先元素是同一个事件监听器实例的目标时,必须分派此事件。

注

另请参阅 mouseout 事件类型。

11.4.11 mouseup

类型 mouseup
接口 MouseEvent
冒泡 是
可取消 是
组合 是
上下文
(可信事件)

当指向设备 按钮在某个元素上被释放时,用户代理必须分派此事件。

注

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

12. 滚轮事件和接口

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

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

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

注

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

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

注

各个用户代理可以(取决于其环境和硬件配置) 对滚轮上的同一物理用户交互作出不同解释。 例如,在触控板边缘从上到下的垂直滑动可以被 解释为旨在向下滚动 页面或向上平移页面的滚轮动作(即分别产生正值或负值的 deltaY)。

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

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

12.1 WheelEvent 接口

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

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

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

[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
增量的测量单位必须是像素。 这是大多数操作系统和 实现配置中最常见的情况。
DOM_DELTA_LINE
增量的测量单位必须是单独的 文本行。许多表单控件属于这种情况。
DOM_DELTA_PAGE
增量的测量单位必须是页面, 可定义为单个屏幕或划分出的页面。
deltaX
在将滚动作为 wheel 事件默认动作的用户代理中,当事件未被取消时,该值必须是沿 x 轴要滚动的测量值(以像素、行或页面为单位)。 否则,这是滚轮设备绕 x 轴移动的 特定于实现的测量值(以像素、行或页面为单位)。 此属性的未初始化值必须为 0.0。
deltaY
在将滚动作为 wheel 事件默认动作的用户代理中,当事件未被取消时,该值必须是沿 y 轴要滚动的测量值(以像素、行或页面为单位)。 否则,这是滚轮设备绕 y 轴移动的 特定于实现的测量值(以像素、行或页面为单位)。 此属性的未初始化值必须为 0.0。
deltaZ

在将滚动作为 wheel 事件默认动作的用户代理中,当事件未被取消时,该值必须是沿 z 轴要滚动的测量值(以像素、行或页面为单位)。 否则,这是滚轮设备绕 z 轴移动的 特定于实现的测量值(以像素、行或页面为单位)。

此属性的未初始化值必须为 0.0。

初始化 deltaZ 属性,该属性属于 WheelEvent 对象。此属性(以及 deltaX 和 deltaY 属性)的相对正值 由右手坐标系给出,其中 X、Y 和 Z 轴分别指向文档的最右边缘、最下边缘 和最远深度(远离用户)。 负的相对值则位于各自相反的方向。

deltaMode

deltaMode 属性包含 增量值测量单位的指示。默认值为 DOM_DELTA_PIXEL(像素)。

此属性必须设置为 DOM_DELTA 常量之一,以 表示增量值的测量单位。 精确测量方式取决于设备、操作系统 和应用程序配置。

此属性的未初始化值必须为 0。

momentum

如果此事件由平台合成,用于在 用户结束物理滚动交互后模拟滚动惯性(例如 快速滑动后从触控板抬起手指),则此属性必须为 true, 否则为 false。

此属性的未初始化值必须为 false。

12.2 滚轮事件类型

12.3 wheel

类型 wheel
接口 WheelEvent
冒泡 是
可取消 视情况而定
组合 是
上下文
(可信事件)

当鼠标滚轮绕任意轴旋转,或者等效输入设备 (例如轨迹球、某些平板设备或触控板等) 模拟此类动作时,用户代理必须分派此事件。根据平台和输入设备不同, 对角线方向的滚轮增量可以作为一个 在多个轴上具有非零值的 wheel 事件传递,也可以针对每个非零轴作为单独的 wheel 事件传递。

wheel 事件类型的典型默认 动作是按指定 量滚动(或在某些情况下缩放)文档。如果取消此事件,实现不得 滚动或缩放文档(也不得执行与此事件 类型关联的任何其他特定于实现的默认动作)。

注

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

12.3.1 滚轮事件的可取消性

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

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

13. 示例

本节为非规范性内容。

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

示例 11:功能 检测和事件绑定
/* 绑定到指针事件或传统的触摸/鼠标事件 */

if (window.PointerEvent) {
    // 如果支持指针事件,则只监听指针事件
    target.addEventListener("pointerdown", function(e) {
        // 如有必要,根据 e.pointerType 应用不同逻辑
        // 以处理不同的触摸/笔/鼠标行为
        ...
    });
    ...
} else {
    // 传统的触摸/鼠标事件处理程序
    target.addEventListener('touchstart', function(e) {
        // 阻止兼容性鼠标事件和 click
        e.preventDefault();
        ...
    });
    ...
    target.addEventListener('mousedown', ...);
    ...
}

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

function detectInputType(event) {
    switch(event.pointerType) {
        case "mouse":
            /* 检测到鼠标输入 */
            break;
        case "pen":
            /* 检测到笔/触控笔输入 */
            break;
        case "touch":
            /* 检测到触摸输入 */
            break;
        default:
            /* pointerType 为空(无法检测)
            或为用户代理特定的自定义类型 */
    }
}
示例 13:调整 元素大小以匹配接触几何形状
<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>
示例 14:从脚本触发 不可信指针事件
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);
示例 15:在 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>

14. 安全和隐私 注意事项

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

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

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

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

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

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

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

15. 术语表

本节为非规范性内容。

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

可测量属性表示与连续指针传感器数据相关的值,这些值使用 实数或来自大范围域的 整数表示。对于指针事件,width、height、 pressure、 tangentialPressure、tiltX、tiltY、twist、 altitudeAngle、azimuthAngle,以及鼠标事件模型属性 screenX、screenY、clientX、clientY 都是 可测量属性。

相比之下,pointerId、pointerType、 isPrimary,以及 鼠标事件模型属性 button、buttons、ctrlKey、 shiftKey、altKey 和 metaKey 不被视为可测量 属性, 因为它们与传感器数据无关。

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

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。此 值可以为 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 索引

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

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

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

[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 WheelEventInit : MouseEventInit {
    double deltaX = 0.0;
    double deltaY = 0.0;
    double deltaZ = 0.0;
    unsigned long deltaMode = 0;
    boolean momentum = false;
};

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

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

D. 参考文献

D.1 规范性参考文献

[CSS-OVERFLOW-3]
CSS 溢出模块第 3 级. Elika Etemad; Florian Rivoal. W3C. 2025 年 10 月 7 日. W3C 工作草案. URL: https://www.w3.org/TR/css-overflow-3/
[css-position]
CSS 定位布局模块第 3 级. Elika Etemad; Tab Atkins Jr. W3C. 2025 年 10 月 7 日. W3C 工作草案. URL: https://www.w3.org/TR/css-position-3/
[CSS21]
层叠样式表第 2 级修订版 1(CSS 2.1) 规范. Bert Bos; Tantek Çelik; Ian Hickson; Håkon Wium Lie. W3C. 2011 年 6 月 7 日. W3C 推荐标准. URL: https://www.w3.org/TR/CSS2/
[CSSOM-View]
CSSOM 视图模块. Simon Fraser; Emilio Cobos Álvarez. W3C. 2025 年 9 月 16 日. W3C 工作草案. URL: https://www.w3.org/TR/cssom-view-1/
[DOM]
DOM 标准. Anne van Kesteren. WHATWG. 现行标准. URL: https://dom.spec.whatwg.org/
[ECMASCRIPT]
ECMAScript 语言规范. Ecma 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.spec.whatwg.org/multipage/
[HTML401]
HTML 4.01 规范. Dave Raggett; Arnaud Le Hors; Ian Jacobs. W3C. 2018 年 3 月 27 日. W3C 推荐标准. URL: https://www.w3.org/TR/html401/
[infra]
Infra 标准. Anne van Kesteren; Domenic Denicola. WHATWG. 现行标准. URL: https://infra.spec.whatwg.org/
[PointerLock]
指针锁定 2.0. Mustaq Ahmed; Vincent Scheib. W3C. 2026 年 2 月 25 日. W3C 工作草案. URL: https://www.w3.org/TR/pointerlock-2/
[RFC2119]
用于 RFC 中表示 要求级别的关键词. S. Bradner. IETF. 1997 年 3 月. 当前最佳实践. URL: https://www.rfc-editor.org/info/rfc2119/
[RFC8174]
RFC 2119 关键词中大写与小写的歧义. B. Leiba. IETF. 2017 年 5 月. 当前最佳实践. URL: https://www.rfc-editor.org/info/rfc8174/
[TOUCH-EVENTS]
触摸事件. Doug Schepers; Sangwhan Moon; Matt Brubeck; Arthur Barstow. W3C. 2013 年 10 月 10 日. W3C 推荐标准. URL: https://www.w3.org/TR/touch-events/
[UIEvents]
UI 事件. Xiaoqian Wu. W3C. 2026 年 2 月 21 日. W3C 工作草案. URL: https://www.w3.org/TR/uievents/
[UIEvents-Key]
UI 事件 KeyboardEvent 键值. Travis Leithead; Gary Kacmarcik. W3C. 2025 年 4 月 22 日. W3C 推荐标准. URL: https://www.w3.org/TR/uievents-key/
[WEBDRIVER-BIDI]
WebDriver BiDi. James Graham; Alex Rudenko; Maksim Sadym. W3C. 2026 年 9 月 30 日. W3C 工作草案. URL: https://www.w3.org/TR/webdriver-bidi/
[WEBIDL]
Web IDL 标准. Edgar Chen; Timothy Gu. WHATWG. 现行标准. URL: https://webidl.spec.whatwg.org/

D.2 资料性参考文献

[COMPAT]
兼容性标准. Mike Taylor. WHATWG. 现行标准. URL: https://compat.spec.whatwg.org/
[PointerEvents]
指针事件. Patrick Lauke; Robert Flack. W3C. 2026 年 10 月 5 日. W3C 工作草案. URL: https://www.w3.org/TR/pointerevents4/
[PointerEvents3]
指针事件. Patrick Lauke; Robert Flack. W3C. 2026 年 6 月 30 日. W3C 推荐标准. URL: https://www.w3.org/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://www.w3.org/TR/WCAG22/