1. 基础设施
本规范依赖于现行标准的基础设施标准。[INFRA]
本规范中使用的一些术语在编码、选择器、受信任类型、Web IDL、XML和XML中的命名空间中有定义。 [ENCODING] [SELECTORS4] [TRUSTED-TYPES] [WEBIDL] [XML] [XML-NAMES]
当需要扩展时,可以相应地更新 DOM 标准,或者可以编写一个新标准,该标准可以利用为 适用的规范 提供的可扩展性钩子。
1.1. 树
树 是一个有限的层次树结构。树的顺序是对树的先序、深度优先遍历。
参与树的对象有一个父节点,它要么是 null,要么是一个对象,并且有子节点,它是一个对象的有序集合。对象A的父节点是对象B,则A是B的子节点。
对象的根是它自己,如果它的父为null,否则它的根是它的父的根。树的根是参与该树的任意对象,而该对象的父为null。
如果对象A是对象B的子,或者对象A是对象C的子,而C是B的后代,则对象A被称为对象B的后代。
包含后代是指一个对象或其后代之一。
当且仅当B是A的后代时,称对象A为对象B的祖先。
包含祖先是指一个对象或其祖先之一。
当且仅当B和A共享相同的非空父节点时,称对象A为对象B的同胞。
包含同胞是指一个对象或其同胞之一。
如果A和B在同一个树中,并且A在树的顺序中位于B之前,则称对象A先于对象B。
如果A和B在同一个树中,并且A在树的顺序中位于B之后,则称对象A继于对象B。
一个对象的第一个子节点是其第一个子节点,如果它没有子节点,则为 null。
一个对象的最后一个子节点是其最后一个子节点,如果它没有子节点,则为 null。
一个对象的前一个同胞是指其第一个先于的同胞,如果没有,则为 null。
一个对象的下一个同胞是指其第一个继于的同胞,如果没有,则为 null。
1.2. 有序集合
有序集合解析器接受一个字符串input,然后执行以下步骤:
-
令inputTokens为在 ASCII 空白字符处分割input的结果。
-
令tokens为一个新的有序集合。
- 返回tokens。
有序集合序列化器接受一个set,并返回使用U+0020 SPACE连接的set的串联结果。
1.3. 选择器
给定字符串 selectors 和 节点 node,作用域匹配选择器字符串 的步骤如下:
-
令 selector 为 解析选择器 selectors 的结果。 [SELECTORS4]
-
如果 selector 返回失败,则 抛出一个 "
SyntaxError"DOMException。 -
返回 selector 与 node 的 根节点 通过 作用域根 node 进行 在树中匹配选择器 的结果。[SELECTORS4]。
不打算支持在选择器中使用命名空间,也不会添加此功能。
1.4. 名称验证
当一个字符串name满足下列步骤返回 true 时,称为有效的元素本地名:
此概念用于在通过 DOM API 构建时验证元素的本地名称。其意图是允许任何可以使用 HTML 解析器构造的名称(第一个码点是ASCII 字母的分支),以及一些额外的可能性。对于那些额外的可能性,出于历史原因,ASCII 范围受到限制,但超出 ASCII 的任何内容都是允许的。
以下与 JavaScript 兼容的正则表达式是有效元素本地名称的实现:
/^(?:[A-Za-z][^\0\t\n\f\r\u0020/>]*|[:_\u0080-\u{10FFFF}][A-Za-z0-9-.:_\u0080-\u{10FFFF}]*)$/u
一个字符串,如果它不包含ASCII 空白字符、 U+0000 NULL 或 U+003E (>),则它是一个有效的文档类型名称。
空字符串是一个有效的文档类型名称。
要验证并提取一个 namespace 和 qualifiedName,给定一个 context:
-
如果 namespace 是空字符串,则将其设置为 null。
-
将 prefix 设置为 null。
-
将 localName 设置为 qualifiedName。
-
如果 qualifiedName 包含 U+003A (:):
-
将 splitResult 设置为运行 严格拆分的结果,参数为 qualifiedName 和 U+003A (:)。
-
将 prefix 设置为 splitResult[0]。
-
将 localName 设置为 splitResult[1]。
-
如果 prefix 不是一个 有效的命名空间前缀,则 抛出一个 "
InvalidCharacterError"DOMException。
-
-
如果 context 是 "
attribute" 并且 localName 不是 有效的属性本地名称,则 抛出一个 "InvalidCharacterError"DOMException。 -
如果 context 是 "
element" 并且 localName 不是 有效的元素本地名称,则 抛出一个 "InvalidCharacterError"DOMException。 -
如果 prefix 不为 null 且 namespace 为 null,则 抛出一个 "
NamespaceError"DOMException。 -
如果 prefix 是 "
xml" 且 namespace 不是 XML 命名空间,则 抛出一个 "NamespaceError"DOMException。 -
如果 qualifiedName 或 prefix 是 "
xmlns" 并且 namespace 不是 XMLNS 命名空间,则 抛出一个 "NamespaceError"DOMException。 -
如果 namespace 是 XMLNS 命名空间 并且 qualifiedName 和 prefix 均不是 "
xmlns",则 抛出一个 "NamespaceError"DOMException。 -
返回 (namespace, prefix, localName)。
本规范中的各种 API 过去对命名空间前缀、属性本地名称、元素本地名称和文档类型名称的验证更为严格。这样做的方式与各种 XML 相关规范保持一致。(尽管并非所有这些规范中的规则都被强制执行。)
这被发现对 Web 开发人员来说很烦人,特别是因为这意味着有些名称可以通过 HTML 解析器创建,但不能通过 DOM API 创建。因此,验证已放宽到仅限于上述描述的那些。
2. 事件
2.1. “DOM 事件”简介
在整个 Web 平台中,事件被派发到对象,以标识发生的事件,例如网络活动或用户交互。这些对象实现了EventTarget接口,因此可以通过调用addEventListener()来添加事件监听器以观察事件:
obj. addEventListener( "load" , imgFetched) function imgFetched( ev) { // great success …}
事件监听器可以通过使用removeEventListener()方法删除,传递相同的参数。
或者,也可以通过将AbortSignal传递给addEventListener(),然后调用控制器上持有信号的abort()来删除事件监听器。
事件也是对象,并实现了Event接口(或派生接口)。在上面的例子中,ev是事件。ev作为参数传递给事件监听器的回调(通常是如上所示的JavaScript函数)。事件监听器通过事件的type属性值(上例中的"load")来区分事件。事件的target属性值返回事件被派发到的对象(如上例中的obj)。
虽然事件通常由用户代理在用户交互或某些任务完成时派发,但应用程序可以通过使用通常称为合成事件的方式派发事件。
// add an appropriate event listener obj. addEventListener( "cat" , function ( e) { process( e. detail) }) // create and dispatch the event var event= new CustomEvent( "cat" , { "detail" : { "hazcheeseburger" : true }}) obj. dispatchEvent( event)
除了用于传递信号外,事件有时也用于让应用程序控制操作中的后续步骤。例如,作为表单提交的一部分,type属性值为"submit"的事件被派发。如果调用了该事件的preventDefault()方法,则表单提交将被终止。希望通过应用程序事件(合成事件)派发该功能的应用程序,可以使用dispatchEvent()方法的返回值。
if ( obj. dispatchEvent( event)) { // event was not canceled, time for some magic …}
当一个事件被派发到一个参与树(例如,一个元素)的对象时,它也可以到达该对象的祖先上的事件监听器。实际上,该对象的所有包含祖先中捕获为true的事件监听器都将按照树的顺序被调用。然后,如果事件的bubbles为true,则该对象的所有包含祖先中捕获为false的事件监听器将按照相反的树的顺序被调用。
<!doctype html> < html > < head > < title > Boring example</ title > </ head > < body > < p > Hello< span id = x > world</ span > !</ p > < script > function test( e) { debug( e. target, e. currentTarget, e. eventPhase) } document. addEventListener( "hey" , test, { capture: true }) document. body. addEventListener( "hey" , test) var ev= new Event( "hey" , { bubbles: true }) document. getElementById( "x" ). dispatchEvent( ev) </ script > </ body > </ html >
debug函数将被调用两次。每次事件的target属性值将是span元素。第一次currentTarget属性值将是document,第二次将是body元素。eventPhase属性值将从CAPTURING_PHASE切换到BUBBLING_PHASE。如果为span元素注册了一个事件监听器,eventPhase属性值将是AT_TARGET。
2.2. 接口 Event
[Exposed=*]interface {Event (constructor DOMString ,type optional EventInit = {});eventInitDict readonly attribute DOMString type ;readonly attribute EventTarget ?target ;readonly attribute EventTarget ?srcElement ; // legacyreadonly attribute EventTarget ?currentTarget ;sequence <EventTarget >composedPath ();const unsigned short NONE = 0;const unsigned short CAPTURING_PHASE = 1;const unsigned short AT_TARGET = 2;const unsigned short BUBBLING_PHASE = 3;readonly attribute unsigned short eventPhase ;undefined stopPropagation ();attribute boolean cancelBubble ; // legacy alias of .stopPropagation()undefined stopImmediatePropagation ();readonly attribute boolean bubbles ;readonly attribute boolean cancelable ;attribute boolean returnValue ; // legacyundefined preventDefault ();readonly attribute boolean defaultPrevented ;readonly attribute boolean composed ; [LegacyUnforgeable ]readonly attribute boolean isTrusted ;readonly attribute DOMHighResTimeStamp timeStamp ;undefined initEvent (DOMString ,type optional boolean =bubbles false ,optional boolean =cancelable false ); // legacy };dictionary {EventInit boolean =bubbles false ;boolean =cancelable false ;boolean =composed false ; };
一个Event对象通常被称为一个事件。它用于标识某些事情已经发生,例如,一个图像已经完成下载。
一个潜在事件目标是 null
或一个EventTarget对象。
一个事件有一个关联的目标(一个潜在事件目标)。除非另有说明,否则它为 null。
一个事件有一个关联的相关目标(一个潜在事件目标)。除非另有说明,否则它为 null。
其他规范使用相关目标来定义一个relatedTarget属性。[UIEVENTS]
一个事件有一个关联的触摸目标列表(一个列表,包含零个或多个潜在事件目标)。除非另有说明,否则它为空列表。
触摸目标列表专门用于定义TouchEvent接口和相关接口。[TOUCH-EVENTS]
一个事件有一个关联的路径。一个路径是一个列表,包含结构体。每个结构体由一个调用目标(一个EventTarget对象)、一个在 shadow 树中的调用目标(一个布尔值)、一个shadow 调整后的目标(一个潜在事件目标)、一个相关目标(一个潜在事件目标)、一个触摸目标列表(一个列表,包含潜在事件目标)、一个关闭树的根(一个布尔值)和一个关闭树中的插槽(一个布尔值)。一个路径最初为空列表。
event = new Event(type [, eventInitDict])- 返回一个新的event,其
type属性值设置为type。eventInitDict参数允许通过同名对象成员设置bubbles和cancelable属性。 event .type- 返回event的类型,例如"
click"、"hashchange"或"submit"。 event .target- 返回event被分派的对象(其目标)。
event .currentTarget- 返回当前正在调用其事件侦听器的回调函数的对象。
event .composedPath()- 返回event的调用目标对象(将在其上调用侦听器的对象),但不包括任何节点,这些节点在shadow 树中,其shadow root的模式为"
closed"且无法从event的currentTarget到达。 event .eventPhase- 返回事件的阶段,它是以下之一:
NONE、CAPTURING_PHASE、AT_TARGET和BUBBLING_PHASE。 event . stopPropagation()- 当在树中分派时,调用此方法会阻止event到达当前对象以外的任何对象。
event . stopImmediatePropagation()- 调用此方法会阻止event在当前侦听器运行结束后到达任何注册的事件侦听器,并且当在树中分派时,也会阻止event到达任何其他对象。
event .bubbles- 根据event的初始化方式返回 true 或 false。如果event通过其目标的祖先按相反的树顺序传播,则为 true;否则为 false。
event .cancelable- 根据event的初始化方式返回 true 或 false。其返回值并不总是有意义,但 true 可以表示在分派event的过程中,部分操作可以通过调用
preventDefault()方法来取消。 event . preventDefault()- 如果在
cancelable属性值为 true 时调用,并且在执行带有passive设置为 false 的侦听器时,通知导致分派event的操作需要取消。 event .defaultPrevented- 如果成功调用
preventDefault()表示取消,则返回 true;否则返回 false。 event .composed- 根据event的初始化情况返回 true 或 false。如果event调用了超过作为其目标的根的
ShadowRoot节点的监听器,则返回 true;否则返回 false。 event .isTrusted- 如果event由用户代理分派,则返回 true,否则返回 false。
event .timeStamp- 以毫秒为单位返回event的时间戳,相对于发生时间。
type属性必须返回初始化时的值。当创建一个事件时,必须将该属性初始化为空字符串。
currentTarget属性必须返回初始化时的值。当创建一个事件时,必须将该属性初始化为 null。
composedPath()方法的步骤为:
-
让composedPath成为一个空的列表。
-
如果path为空,则返回composedPath。
-
让currentTarget成为this的
currentTarget属性值。 -
断言:currentTarget 是一个
EventTarget对象。 -
追加currentTarget到composedPath。
-
将currentTargetIndex设为 0。
-
将currentTargetHiddenSubtreeLevel设为 0。
-
将index设为path的大小 - 1。
-
当index大于或等于 0 时:
-
将currentHiddenLevel和maxHiddenLevel设为currentTargetHiddenSubtreeLevel。
-
将index设为currentTargetIndex - 1。
-
当index大于或等于 0 时:
-
如果path[index]的关闭树的根为 true,则将currentHiddenLevel增加 1。
-
如果currentHiddenLevel小于或等于maxHiddenLevel,则前置path[index]的调用目标到composedPath。
-
如果 path[index] 的 slot-in-closed-tree 为 true:
-
将currentHiddenLevel减少 1。
-
如果currentHiddenLevel小于maxHiddenLevel,则将maxHiddenLevel设为currentHiddenLevel。
-
-
减少index的值 1。
-
-
将currentHiddenLevel和maxHiddenLevel设为currentTargetHiddenSubtreeLevel。
-
将index设为currentTargetIndex + 1。
-
当index小于path的大小时:
-
如果path[index]的关闭树中的插槽为 true,则将currentHiddenLevel增加 1。
-
如果currentHiddenLevel小于或等于maxHiddenLevel,则追加path[index]的调用目标到composedPath。
-
如果 path[index] 的 root-of-closed-tree 为 true:
-
将currentHiddenLevel减少 1。
-
如果currentHiddenLevel小于maxHiddenLevel,则将maxHiddenLevel设为currentHiddenLevel。
-
-
增加index的值 1。
-
-
返回composedPath。
eventPhase属性必须返回初始化时的值,该值必须是以下之一:
NONE(数值 0)- 事件当前未被分派时处于此阶段。
CAPTURING_PHASE(数值 1)- 当一个事件被分派到一个参与树的对象时,它将在到达其目标之前处于此阶段。
AT_TARGET(数值 2)- 当一个事件被分派时,它将在其目标上处于此阶段。
BUBBLING_PHASE(数值 3)- 当一个事件被分派到一个参与树的对象时,它将在到达其目标后处于此阶段。
最初该属性必须初始化为NONE。
每个事件都具有以下关联标志,最初都未设置:
- 停止传播标志
- 立即停止传播标志
- 取消标志
- 在被动侦听器中的标志
- 合成标志
- 初始化标志
- 分派标志
stopPropagation()方法的步骤是设置this的停止传播标志。
cancelBubble获取步骤是如果this的停止传播标志已设置,则返回 true;否则返回 false。
cancelBubble设置步骤是如果给定值为
true,则设置this的停止传播标志;否则不执行任何操作。
stopImmediatePropagation()方法的步骤是设置this的停止传播标志和this的立即停止传播标志。
bubbles和cancelable属性必须返回初始化时的值。
要设置取消标志,给定一个事件event,如果event的cancelable属性值为
true 并且event的在被动侦听器中的标志未设置,则设置event的取消标志,否则不执行任何操作。
returnValue获取步骤是如果this的取消标志已设置,则返回 false;否则返回 true。
returnValue设置步骤是如果给定值为
false,则设置取消标志,并将this作为参数;否则不执行任何操作。
preventDefault()方法的步骤是将取消标志设置为this。
在某些情况下,调用preventDefault()无效。建议用户代理在开发者控制台中记录具体原因,以帮助调试。
defaultPrevented获取步骤是如果this的取消标志已设置,则返回 true;否则返回 false。
composed获取步骤是如果this的合成标志已设置,则返回 true;否则返回 false。
isTrusted属性必须返回初始化时的值。当创建一个事件时,必须将该属性初始化为 false。
isTrusted是一个方便的属性,指示一个事件是否由用户代理分派(而不是使用dispatchEvent())。唯一的传统例外是click(),它导致用户代理分派一个isTrusted属性初始化为
false 的事件。
timeStamp属性必须返回初始化时的值。
要初始化一个event,使用type、bubbles和cancelable,运行以下步骤:
initEvent()与事件构造函数是多余的,并且无法设置composed。它必须为了遗留内容而支持。
2.3.
对 Window
接口的传统扩展
partial interface Window { [Replaceable ]readonly attribute (Event or undefined )event ; // legacy };
每个 Window
对象都有一个关联的 当前事件(未定义或一个 Event
对象)。除非另有说明,否则它是未定义的。
event 的 getter 步骤是返回 this 的 当前事件。
强烈建议 Web 开发者依赖传递给事件监听器的 Event
对象,因为这将产生更具可移植性的代码。此属性在 workers 或 worklets 中不可用,并且对于在 shadow trees 中分派的事件是不准确的。
2.4. 接口 CustomEvent
[Exposed=*]interface :CustomEvent Event {(constructor DOMString ,type optional CustomEventInit = {});eventInitDict readonly attribute any detail ;undefined initCustomEvent (DOMString ,type optional boolean =bubbles false ,optional boolean =cancelable false ,optional any =detail null ); // legacy };dictionary :CustomEventInit EventInit {any =detail null ; };
事件 使用 CustomEvent
接口可以用于携带自定义数据。
event = new CustomEvent(type [, eventInitDict])- 其工作方式类似于
Event的构造函数,但 eventInitDict 参数现在还允许设置detail属性。 event .detail- 返回创建 event 时的任何自定义数据。 通常用于合成事件。
detail 属性必须返回它初始化时的值。
initCustomEvent(type, bubbles, cancelable, detail)
方法的步骤如下:
2.5. 构造事件
规范可能会为所有或某些事件定义事件构造步骤。该算法接收一个事件event和一个EventIniteventInitDict,如内部事件创建步骤中所示。
这个构造方式可用于Event子类,它们具有比简单的初始化字典成员和IDL属性之间一对一映射更复杂的结构。
要使用eventInterface 创建事件,该接口必须是Event或继承自它的接口,并且可选地提供realm
realm,运行以下步骤:
-
如果未提供realm,则将其设置为null。
-
将dictionary设置为将JavaScript值undefined转换为eventInterface构造函数所接受的字典类型的结果。(此字典类型将是
EventInit或继承自它的字典类型。)如果需要成员,这种方法不起作用;请参见whatwg/dom#600。
-
运行内部事件创建步骤,以eventInterface、realm、该事件信号发生的时间和dictionary为参数,得到event。
-
将event的
isTrusted属性初始化为true。 -
返回event。
创建事件用于需要分别创建和分发事件的其他规范,而不是简单地触发事件。它确保事件的属性初始化为正确的默认值。
内部事件创建步骤,给定eventInterface、realm、time和dictionary,如下所示:
-
使用eventInterface创建一个新对象作为event的结果。如果realm非空,则使用该realm;否则,使用Web IDL中定义的默认行为。
截至本文撰写时,Web IDL 尚未定义任何默认行为;请参见whatwg/webidl#135。
-
设置event的初始化标志。
-
将event的
timeStamp属性初始化为给定time和event的相关全局对象的相对高分辨率粗略时间。 -
对于 dictionary 中的每个 member → value: 如果 event 具有 标识符为 member 的属性,则将该属性初始化为 value。
-
运行事件构造步骤,参数为event和dictionary。
-
返回event。
2.6. 定义事件接口
通常,在定义一个继承自Event的新接口时,请务必征求WHATWG或W3C WebApps
WG社区的反馈。
CustomEvent接口可以作为起点。然而,不要引入任何init*Event()方法,因为它们与构造函数是冗余的。继承自Event接口的接口中如果包含此类方法,也只是出于历史原因。
2.7. 接口 EventTarget
[Exposed=*]interface {EventTarget constructor ();undefined addEventListener (DOMString ,type EventListener ?,callback optional (AddEventListenerOptions or boolean )= {});options undefined removeEventListener (DOMString ,type EventListener ?,callback optional (EventListenerOptions or boolean )= {});options boolean dispatchEvent (Event ); };event callback interface {EventListener undefined (handleEvent Event ); };event dictionary {EventListenerOptions boolean =capture false ; };dictionary :AddEventListenerOptions EventListenerOptions {boolean ;passive boolean =once false ;AbortSignal ; };signal
一个 EventTarget
对象表示一个目标,当某些事情发生时,一个事件可以被派发到该目标。
每个 EventTarget
对象都有一个相关的事件监听器列表(一个由零个或多个事件监听器组成的列表)。它最初是空的列表。
一个事件监听器 可以用于观察特定的事件,它由以下部分组成:
- 类型(一个字符串)
- 回调(null或一个
EventListener对象) - 捕获(一个布尔值,初始值为false)
- 被动(null或一个布尔值,初始值为null)
- 一次性(一个布尔值,初始值为false)
- 信号(null或一个
AbortSignal对象) - 已移除(用于记录的布尔值,初始值为false)
虽然回调是一个 EventListener
对象,事件监听器
是一个更广泛的概念,如上所述。
每个 EventTarget
对象还有一个相关的获取父级算法,
该算法接收一个事件
event,并返回一个EventTarget
对象。除非另有说明,它返回null。
每个 EventTarget
对象可以有一个相关的激活行为算法。该激活行为算法接收一个事件,如
派发
算法中所示。
这是因为用户代理在某些EventTarget
对象上执行某些操作,例如
area
元素,以响应其MouseEvent
的type
属性为click的合成事件。由于Web兼容性问题,它未能被移除,并且现在成为定义激活某些内容的固定方式。[HTML]
每个 EventTarget
对象如果具有激活行为,还可以同时(而非单独)具有遗留预激活行为算法
和遗留取消激活行为
算法。
这些算法仅存在于复选框和单选框的
input
元素中,
不适用于其他任何内容。[HTML]
target = new EventTarget();-
创建一个新的
EventTarget对象,开发者可以使用它来派发并 监听事件。 target . addEventListener(type, callback [, options])-
为
type属性值为type的事件附加一个事件侦听器。callback参数设置将在事件分派时调用的回调函数。当options为true时,监听器在捕获阶段触发;当为false或未设置时,监听器在冒泡阶段触发。无论设置如何,若事件处于目标阶段,监听器均会被触发。
当options设置为true时,表示监听器为被动模式,不会调用
preventDefault()来取消事件。当options设置为true时,表示监听器仅触发一次,触发后将被移除。
若为options指定了
AbortSignal,当信号被中止时,监听器将被移除。若已存在相同type、callback和capture的事件监听器,则不会再次添加。
target . removeEventListener(type, callback [, options])-
移除target中与type、callback和options匹配的事件监听器。
target . dispatchEvent(event)-
派发event事件,并返回true,若事件的
cancelable属性为false或未调用preventDefault(),否则返回false。
要展开 options,请执行以下步骤:
-
如果options是布尔值,则返回options。
-
返回options["
capture"]。
要进一步展开 options,请执行以下步骤:
new EventTarget()构造函数步骤不执行任何操作。
由于其他地方声明的默认值,返回的EventTarget的
获取父级算法将返回null,并且它没有激活行为、遗留预激活行为,
或遗留取消激活行为。
将来我们可能会允许自定义获取父级算法。请告诉我们
这对您的程序是否有用。目前,所有作者创建的EventTarget
不参与树结构。
默认被动值,给定一个事件类型type和一个EventTarget
eventTarget,由下列步骤决定:
要添加事件监听器,给定一个EventTarget
对象eventTarget和一个事件监听器listener,请执行以下步骤:
-
若eventTarget是一个
ServiceWorkerGlobalScope对象,其service worker的脚本资源的曾经评估过的标志被设置,且listener的类型与任何service worker 事件的类型属性值匹配,则向控制台报告一个警告,提示这可能不会产生预期结果。[SERVICE-WORKERS] -
若listener的回调为null,则返回。
-
若listener的被动为null,则将其设置为listener的默认被动值,根据listener的类型 和eventTarget。
-
若eventTarget的事件监听器列表不包含一个事件监听器,其类型是listener的类型,回调是listener的回调,且捕获是listener的捕获,则追加 listener到eventTarget的事件监听器列表中。
-
- 移除事件监听器,使用eventTarget和listener。
添加事件监听器的概念是为了确保事件处理器使用相同的代码路径。[HTML]
addEventListener(type, callback, options)
方法步骤为:
要移除事件监听器,给定一个EventTarget
对象eventTarget和一个事件监听器 listener,请执行以下步骤:
-
如果eventTarget是
ServiceWorkerGlobalScope对象,并且它的Service Worker的要处理的事件类型集合包含listener的类型,那么报告一个警告到控制台,表明这可能不会产生预期的结果。[SERVICE-WORKERS]
HTML 需要这个来定义事件处理器。[HTML]
要移除所有事件监听器,给定一个EventTarget
对象eventTarget: 对其每个
listener的事件监听器列表项: 移除事件监听器,使用eventTarget和listener。
HTML 需要这个来定义document.open()。[HTML]
removeEventListener(type, callback, options)
方法步骤为:
-
令capture为展开options的结果。
-
如果this的事件 监听器列表 包含一个事件监听器, 其类型是type,回调是 callback,且捕获是capture,则移除事件监听器,使用this和该事件监听器。
事件侦听器列表不会包含具有相同type、callback和capture的多个事件侦听器,因为添加事件侦听器会防止这种情况发生。
dispatchEvent(event)方法步骤为:
-
若event的派发标志已设置,或其初始化标志未设置,则抛出一个“
InvalidStateError”DOMException。 -
将event的
isTrusted属性初始化为false。
2.8. 观察事件监听器
一般来说,开发者不会期望事件监听器的存在是可观察的。 事件监听器的影响由其回调决定。也就是说, 开发者添加一个无操作的事件监听器时不会期望它有任何副作用。
不幸的是,一些事件API的设计使得要高效实现它们就需要观察事件侦听器。这使得侦听器的存在是可观察的,即使是空的侦听器也可能对应用程序的行为产生显著的性能影响。例如,可以用于阻止异步滚动的触摸和滚轮事件。在某些情况下,可以通过仅在存在至少一个非被动侦听器时将事件指定为可取消来缓解这一问题。例如,非被动触摸事件侦听器必须阻止滚动,但如果所有侦听器都是被动的,则可以通过使触摸事件不可取消(从而忽略对preventDefault()的调用)来允许滚动并行开始。因此,分派事件的代码能够观察到非被动侦听器的缺失,并使用该信息来清除正在分派的事件的可取消属性。
理想情况下,任何新的事件API都应定义为不需要此属性。(请使用whatwg/dom进行讨论。)
要获取遗留的Service
Worker获取事件侦听器回调,给定一个ServiceWorkerGlobalScopeglobal,请运行以下步骤。这些步骤将返回一个EventListener对象的列表。
2.9. 分发事件
要将一个事件分发给一个目标,可选带有legacy target override flag和legacyOutputDidListenersThrowFlag,请按以下步骤操作:
-
设置event的分发标志。
-
如果未给出legacy target override flag,则将targetOverride设置为目标,否则设置为目标的关联的
文档。[HTML]legacy target override flag仅由HTML使用,且仅当目标是一个
窗口对象时使用。 -
将activationTarget设置为null。
-
将relatedTarget设置为针对目标重新定位event的relatedTarget的结果。
-
设 clearTargets 为 false。
-
如果 target 不是 relatedTarget 或者 target 是 event 的 relatedTarget:
-
将touchTargets设置为一个新的列表。
-
对于每个touchTarget,它在event的触摸目标列表中: 追加针对目标重新定位touchTarget的结果到touchTargets中。
-
通过event、目标、targetOverride、relatedTarget、touchTargets,并设置为false,附加到事件路径。
-
如果event是一个
鼠标事件对象,且event的类型属性为"click",则isActivationEvent为true;否则为false。 -
如果isActivationEvent为true且目标具有激活行为,则将activationTarget设置为目标。
-
将slot-in-closed-tree设置为false。
-
将parent设置为调用目标的获取父项结果的event。
-
当parent非空时:
-
如果slottable非空:
-
将relatedTarget设置为针对parent重新定位event的relatedTarget的结果。
-
将touchTargets设置为一个新的列表。
-
对于每个touchTarget ,它在event的触摸目标列表中: 追加针对parent重新定位touchTarget的结果到touchTargets中。
-
如果parent是一个
窗口对象,或parent是一个节点且目标的根是parent的包括影子在内的包容性祖先,则: -
否则,如果parent是relatedTarget,则将parent设置为null。
-
否则:
-
如果parent非空,则将parent设置为调用parent的获取父项结果的event。
-
将slot-in-closed-tree设置为false。
-
-
如果 clearTargetsStruct 的 shadow-adjusted target、clearTargetsStruct 的 relatedTarget,或 clearTargetsStruct 的 touch target list 中的
EventTarget对象是一个节点,其根是一个影子根:则将 clearTargets 设置为 true。 -
如果activationTarget非空且activationTarget具有旧式预激活行为,则运行activationTarget的旧式预激活行为。
-
-
将event的
当前目标属性设置为null。 -
将event的路径设置为空列表。
-
如果 clearTargets 为 true:
-
将event的目标设置为null。
-
将event的relatedTarget设置为null。
-
将event的触摸目标列表设置为空列表。
-
-
如果activationTarget非空:
-
如果event的取消标志已设置,则返回false;否则返回true。
要附加到事件路径,给定一个event、invocationTarget、shadowAdjustedTarget、relatedTarget、touchTargets,以及slot-in-closed-tree,请运行这些步骤:
-
将invocationTargetInShadowTree设置为false。
-
如果invocationTarget是一个节点,且其根是一个影子根,则将invocationTargetInShadowTree设置为true。
-
将root-of-closed-tree设置为false。
-
如果invocationTarget是一个影子根,且其模式是"
closed",则将root-of-closed-tree设置为true。 -
追加一个新的结构到event的路径,其调用目标是invocationTarget,调用目标在影子树中是invocationTargetInShadowTree,影子调整目标是shadowAdjustedTarget,relatedTarget是relatedTarget,触摸目标列表是touchTargets,关闭树的根是root-of-closed-tree,以及slot-in-closed-tree是slot-in-closed-tree。
要调用,给定一个struct、event、阶段,以及可选的legacyOutputDidListenersThrowFlag,请运行这些步骤:
-
将event的目标设置为event的路径中的最后一个结构,其影子调整目标非空,且为struct或struct之前的结构。
-
将event的relatedTarget设置为struct的relatedTarget。
-
如果event的停止传播标志已设置,则返回。
-
将invocationTargetInShadowTree设置为struct的调用目标在影子树中。
-
将found设置为运行内部调用通过event、listeners、阶段、invocationTargetInShadowTree,以及如果给出的话legacyOutputDidListenersThrowFlag的结果。
-
如果 found 为 false 且 event 的
isTrusted属性为 true:-
将originalEventType设置为event的
类型属性值。 -
如果event的
类型属性值匹配下表中的任意字符串,将event的类型属性值设置为同一行匹配字符串旁的字符串,否则返回。事件类型 旧式事件类型 " animationend"" webkitAnimationEnd"" animationiteration"" webkitAnimationIteration"" animationstart"" webkitAnimationStart"" transitionend"" webkitTransitionEnd" -
内部调用通过event、listeners、阶段、invocationTargetInShadowTree,以及如果给出的话legacyOutputDidListenersThrowFlag。
-
将event的
类型属性值设置为originalEventType。
-
要内部调用,给定一个event、listeners、阶段、invocationTargetInShadowTree,以及可选的legacyOutputDidListenersThrowFlag,请运行这些步骤:
-
让 found 为 false。
-
对于 listeners 中的每个 listener,其 removed 为 false:
-
将 found 设为 true。
-
如果 listener 的 once 为 true,则根据 event 的
currentTarget属性值和 listener 移除一个事件侦听器。 -
让 currentEvent 为 undefined。
-
如果 global 是一个
Window对象: -
如果 global 是一个
Window对象,则 记录事件监听器的计时信息,使用 event 和 listener。 -
调用用户对象的操作,使用 listener 的 回调,"
handleEvent",«event»,以及 event 的currentTarget属性值。如果这会抛出一个异常 exception:-
设置 legacyOutputDidListenersThrowFlag(如果提供)。
legacyOutputDidListenersThrowFlag 仅被 Indexed Database API 使用。[INDEXEDDB]
-
取消设定 event 的 被动监听器标志。
-
返回 found。
2.10. 触发事件
要在target上触发事件,事件名为e,可选地使用eventConstructor,并描述如何初始化IDL属性,以及legacy target override flag,请执行以下步骤:
在 DOM 的上下文中,"Fire" 是创建、初始化和分派一个事件的缩写。Fire an event 使得这个过程更容易表达。
如果事件需要其
bubbles
或cancelable
属性初始化,
可以这样写:“fire an
event named submit at target
with its cancelable
attribute initialized to true”。
或者,当需要自定义构造函数时,可以这样写:“fire an event named click at target
using MouseEvent
with its detail
attribute initialized to 1”。
有时返回值很重要:
-
让 doAction 成为 firing an event named
likeat target 的结果。 -
如果 doAction 为真,那么……
2.11. 行为与发生
一个事件表示一次发生,而不是一个行为。换句话说,它
代表来自算法的通知,并可用于影响该算法的未来进程
(例如,通过调用 preventDefault())。
事件不得
用作导致某些算法开始运行的行为或发起者。那不是
它们的用途。
在此特别指出这一点,是因为 DOM 的先前迭代具有与事件关联的“默认行为”概念,这给人们带来了所有错误的想法。事件不代表或导致行为,它们 只能用于影响正在进行的行为。
3. 中止正在进行的活动
尽管 promise 没有内置的中止机制,但许多使用它们的 API 都需要中止语义。AbortController
旨在通过提供一个 abort()
方法来支持这些要求,该方法可以切换相应 AbortSignal
对象的状态。希望支持中止的 API 可以接受一个 AbortSignal
对象,并使用其状态来确定如何继续。
鼓励依赖 AbortController
的 API 通过使用 AbortSignal
的中止原因来拒绝任何未解决的 promise,从而响应 abort()。
一个假设的doAmazingness({ ... })方法可以通过接受AbortSignal对象来支持中止,类似如下:
const controller = new AbortController();
const signal = controller. signal;
startSpinner();
doAmazingness({ ..., signal })
. then( result => ...)
. catch ( err => {
if ( err. name == 'AbortError' ) return ;
showUserErrorMessage();
})
. then(() => stopSpinner());
// …
controller. abort();
doAmazingness could be implemented as follows:
function doAmazingness({ signal}) {
return new Promise(( resolve, reject) => {
signal. throwIfAborted();
// Begin doing amazingness, and call resolve(result) when done.
// But also, watch for signals:
signal. addEventListener( 'abort' , () => {
// Stop doing amazingness, and:
reject( signal. reason);
});
});
}
不返回promise的API可以选择以类似的方式做出反应,或者选择完全不展示AbortSignal的中止原因。addEventListener()就是一个适合后者的API例子。
需要更精细控制的API可以根据需要扩展AbortController和AbortSignal对象。
3.1.
接口 AbortController
[Exposed=*]interface {AbortController constructor (); [SameObject ]readonly attribute AbortSignal signal ;undefined abort (optional any ); };reason
controller = new AbortController()- 返回一个新的controller,其
signal被设置为新创建的AbortSignal对象。 controller . signal- 返回与此对象关联的
AbortSignal对象。 controller . abort(reason)- 调用此方法将把reason存储在此对象的
AbortSignal的中止原因中,并向任何观察者发出信号,表示相关活动将被中止。如果reason未定义,则会存储一个"AbortError"DOMException。
一个AbortController对象有一个关联的信号(一个AbortSignal对象)。
new AbortController()构造函数步骤如下:
-
让signal成为一个新的
AbortSignal对象。
signal的getter步骤是返回this的signal。
abort(reason)方法步骤是在signal abort上执行,带有reason如果给定。
在AbortController的controller上带有可选的reason执行signal abort,如果reason被提供。
3.2. 接口AbortSignal
[Exposed=*]interface :AbortSignal EventTarget { [NewObject ]static AbortSignal abort (optional any ); [reason Exposed =(Window ,Worker ),NewObject ]static AbortSignal timeout ([EnforceRange ]unsigned long long ); [milliseconds NewObject ]static AbortSignal _any (sequence <AbortSignal >);signals readonly attribute boolean aborted ;readonly attribute any reason ;undefined throwIfAborted ();attribute EventHandler onabort ; };
AbortSignal . abort(reason)- 返回一个
AbortSignal实例,其中止原因设置为reason(如果未定义则为"AbortError"DOMException)。 AbortSignal . any(signals)- 返回一个
AbortSignal实例,该实例将在任一signals中止时中止。其中止原因将被设置为导致中止的signals中的某一个。 AbortSignal . timeout(milliseconds)- 返回一个
AbortSignal实例,该实例将在milliseconds毫秒后中止。其中止原因将被设置为"TimeoutError"DOMException。 signal . aborted- 如果signal的
AbortController已发出中止信号,则返回true;否则返回false。 signal . reason- 返回signal的中止原因。
signal . throwIfAborted()- 如果signal的
AbortController已发出中止信号,则抛出signal的中止原因;否则不执行任何操作。
一个AbortSignal对象有一个关联的中止原因(一个JavaScript值),其初始状态为未定义。
一个AbortSignal对象有一个关联的中止算法(一组将在中止时执行的算法),其初始状态为空。
中止算法使得具有复杂要求的API能够合理地响应abort()。例如,给定API的中止原因可能需要传播到跨线程环境,例如service worker。
一个AbortSignal对象有一个依赖项(一个布尔值),其初始状态为false。
一个AbortSignal对象有一个关联的源信号(一组AbortSignal对象),其初始状态为空。
一个AbortSignal对象有一个关联的依赖信号(一组依赖于对象的AbortSignal对象),其初始状态为空。
静态方法abort(reason)的步骤如下:
-
让signal成为一个新的
AbortSignal对象。 -
将signal的中止原因设置为reason,如果给定;否则设置为新的"
AbortError"DOMException。 - 返回signal。
静态方法timeout(milliseconds)的步骤如下:
-
让signal成为一个新的
AbortSignal对象。 -
让global成为signal的相关全局对象。
-
在超时后运行步骤,给定global、"
AbortSignal-timeout"、milliseconds,以及以下步骤:-
在全局任务队列中排队,在global上,基于signal和新的"
TimeoutError"DOMException执行signal abort。
在此超时时间内,如果signal已为其
abort事件注册了任何事件监听器,则必须从global到signal保持一个强引用。 -
-
返回signal。
静态方法any(signals)的步骤是返回创建一个依赖的中止信号的结果,使用signals、AbortSignal和当前领域。
abortedgetter步骤是返回true如果this已中止;否则返回false。
throwIfAborted()方法步骤是抛出this的中止原因,如果this已中止。
此方法主要用于当接受AbortSignal的函数希望在特定检查点抛出(或返回一个被拒绝的promise),而不是将AbortSignal传递给其他方法时。例如,以下函数允许在每次尝试轮询条件之间中止。这为中止轮询过程提供了机会,即使实际的异步操作(即)不接受AbortSignal。
async function waitForCondition( func, targetValue, { signal} = {}) { while ( true ) { signal? . throwIfAborted(); const result= await func(); if ( result=== targetValue) { return ; } } }
属性 onabort 是一个 事件处理程序 IDL 属性,用于 onabort 事件处理程序,其 事件处理程序事件类型 是 abort。
对 AbortSignal
对象的更改代表了相应的 AbortController
对象的意图,但观察 AbortSignal
对象的 API 可以选择忽略这些更改。例如,如果操作已经完成。
当 AbortSignal
对象的 中止原因 不为未定义时,
该对象被认为是 已中止。
要向 AbortSignal
对象 signal 中 添加
一个算法 algorithm:
要从 AbortSignal
signal 中移除一个算法 algorithm,请从 signal 的中止算法中移除
algorithm。
要 发出中止信号,
给定一个 AbortSignal
对象 signal 和一个可选的 reason:
-
如果 signal 已经 中止,则返回。
-
将 signal 的 中止原因 设为 reason(如果提供);否则设为一个新的 "
AbortError"DOMException。 -
将 dependentSignalsToAbort 设为一个新的 列表。
-
运行中止步骤 对于 signal。
-
对于每个 dependentSignal 在 dependentSignalsToAbort 中,运行中止步骤 对于 dependentSignal。
要 运行中止步骤 对于一个
AbortSignal
signal:
要从 AbortSignal
对象列表 signals 创建一个依赖中止信号,使用 signalInterface(它必须是 AbortSignal
或继承自它的接口)和一个 realm:
3.2.1. 垃圾回收
一个未中止的依赖的
AbortSignal
对象,当其源信号列表非空并且它已为其 abort
事件注册了事件监听器或者其中止算法列表非空时,不能被垃圾回收。
3.3.
在 API 中使用 AbortController
和 AbortSignal
对象
任何使用 promises 表示可以中止操作的 Web 平台 API 必须遵循以下规则:
- 通过
signal字典成员接受AbortSignal对象。 - 通过使用
AbortSignal对象的中止原因来拒绝 promise,从而传达操作已中止。 - 如果
AbortSignal已经中止,则立即拒绝,否则: - 使用中止算法机制来观察
AbortSignal对象的更改,并以不导致与其他观察者冲突的方式执行此操作。
不使用 promises 的 API 仍应尽可能遵循上述规则。
4. 节点
4.1. “DOM”简介
在其原始意义上,“DOM”是一个用于访问和操作文档(特别是HTML和XML文档)的API。在本规范中,术语“文档”用于任何基于标记的资源,从简短的静态文档到包含丰富多媒体的长篇文章或报告,以及功能齐全的交互式应用程序。
每个这样的文档都表示为一个节点树。树中的一些节点可以有子节点,而其他的总是叶子节点。
为了说明这一点,考虑以下HTML文档:
<!DOCTYPE html> < html class = e > < head >< title > Aliens?</ title ></ head > < body > Why yes.</ body > </ html >
它表示如下:
请注意,由于HTML解析的神奇之处,并非所有ASCII空白字符都被转化为文本节点,但总体概念是清晰的。标记进入,树形节点输出。
可以使用非常棒的Live DOM Viewer来更详细地探索这个问题。
4.2. 节点树
节点是实现了实现Node的对象。节点参与一个称为节点树的树。
实际上,你处理的是更具体的对象。
实现了实现Node的对象也实现了一个继承的接口:Document、DocumentType、DocumentFragment、Element、CharacterData或Attr。
实现了DocumentFragment的对象有时会实现ShadowRoot。
实现了Element的对象通常也会实现一个继承的接口,例如HTMLAnchorElement。
实现了CharacterData的对象也会实现一个继承的接口:Text、ProcessingInstruction或Comment。
实现了Text的对象有时会实现CDATASection。
因此,每个节点的