DOM

现行标准 — 最后更新

参与:
GitHub whatwg/dom (新问题, 开放的问题)
在 Matrix 上聊天
提交:
GitHub whatwg/dom/commits
此提交的快照
@thedomstandard
测试:
web-platform-tests dom/ (进行中的工作)
翻译 (非规范性)
日本語
简体中文
한국어

摘要

DOM 定义了一个平台中立的模型,用于事件、活动中止和节点树。

1. 基础设施

本规范依赖于现行标准的基础设施标准。[INFRA]

本规范中使用的一些术语在编码选择器受信任类型Web IDLXMLXML中的命名空间中有定义。 [ENCODING] [SELECTORS4] [TRUSTED-TYPES] [WEBIDL] [XML] [XML-NAMES]

当需要扩展时,可以相应地更新 DOM 标准,或者可以编写一个新标准,该标准可以利用为 适用的规范 提供的可扩展性钩子。

1.1.

是一个有限的层次树结构。树的顺序是对的先序、深度优先遍历。

参与的对象有一个父节点,它要么是 null,要么是一个对象,并且有子节点,它是一个对象的有序集合。对象A父节点是对象B,则AB子节点

对象的是它自己,如果它的为null,否则它的根是它的的根。的根是参与该的任意对象,而该对象的为null。

如果对象A是对象B,或者对象A是对象C,而CB后代,则对象A被称为对象B后代

包含后代是指一个对象或其后代之一。

当且仅当BA后代时,称对象A为对象B祖先

包含祖先是指一个对象或其祖先之一。

当且仅当BA共享相同的非空父节点时,称对象A为对象B同胞

包含同胞是指一个对象或其同胞之一。

如果AB在同一个中,并且A树的顺序中位于B之前,则称对象A先于对象B

如果AB在同一个中,并且A树的顺序中位于B之后,则称对象A继于对象B

一个对象的第一个子节点是其第一个子节点,如果它没有子节点,则为 null。

一个对象的最后一个子节点是其最后一个子节点,如果它没有子节点,则为 null。

一个对象的前一个同胞是指其第一个先于同胞,如果没有,则为 null。

一个对象的下一个同胞是指其第一个继于同胞,如果没有,则为 null。

一个对象的索引是其先于同胞的数量,如果没有,则为 0。

1.2. 有序集合

有序集合解析器接受一个字符串input,然后执行以下步骤:

  1. inputTokens在 ASCII 空白字符处分割input的结果。

  2. tokens为一个新的有序集合

  3. 对于 inputTokens 中的每个 token: 将 token 追加tokens

  4. 返回tokens

有序集合序列化器接受一个set,并返回使用U+0020 SPACE连接的set串联结果

1.3. 选择器

给定字符串 selectors节点 node作用域匹配选择器字符串 的步骤如下:

  1. selector解析选择器 selectors 的结果。 [SELECTORS4]

  2. 如果 selector 返回失败,则 抛出一个 "SyntaxError"DOMException

  3. 返回 selectornode根节点 通过 作用域根 node 进行 在树中匹配选择器 的结果。[SELECTORS4]

不打算支持在选择器中使用命名空间,也不会添加此功能。

1.4. 名称验证

当一个字符串满足以下条件时,称为有效的命名空间前缀:其长度至少为1,并且不包含ASCII 空白符、U+0000 NULL、U+002F (/) 或 U+003E (>)。

当一个字符串满足以下条件时,称为有效的属性本地名:其长度至少为1,并且不包含ASCII 空白符、U+0000 NULL、U+002F (/)、U+003D (=) 或 U+003E (>)。

当一个字符串name满足下列步骤返回 true 时,称为有效的元素本地名

  1. 如果name长度为0,则返回 false。

  2. 如果name的第0个码点ASCII 字母

    1. 如果name包含ASCII 空白符、U+0000 NULL、U+002F (/) 或 U+003E (>),则返回 false。

    2. 返回 true。

  3. 如果name的第0个码点不是 U+003A (:)、U+005F (_),也不在 U+0080~U+10FFFF 范围内,则返回 false。

  4. 如果name的后续码点(如有)不是ASCII 字母ASCII 数字、U+002D (-)、U+002E (.)、U+003A (:)、U+005F (_),或者不在 U+0080~U+10FFFF 范围内,则返回 false。

  5. 返回 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 (>),则它是一个有效的文档类型名称

空字符串是一个有效的文档类型名称

验证并提取一个 namespacequalifiedName,给定一个 context

  1. 如果 namespace 是空字符串,则将其设置为 null。

  2. prefix 设置为 null。

  3. localName 设置为 qualifiedName

  4. 如果 qualifiedName 包含 U+003A (:):

    1. splitResult 设置为运行 严格拆分的结果,参数为 qualifiedName 和 U+003A (:)。

    2. prefix 设置为 splitResult[0]。

    3. localName 设置为 splitResult[1]。

    4. 如果 prefix 不是一个 有效的命名空间前缀,则 抛出一个 "InvalidCharacterError" DOMException

  5. 断言prefix 要么为 null,要么是一个 有效的命名空间前缀

  6. 如果 context 是 "attribute" 并且 localName 不是 有效的属性本地名称,则 抛出一个 "InvalidCharacterError" DOMException

  7. 如果 context 是 "element" 并且 localName 不是 有效的元素本地名称,则 抛出一个 "InvalidCharacterError" DOMException

  8. 如果 prefix 不为 null 且 namespace 为 null,则 抛出一个 "NamespaceError" DOMException

  9. 如果 prefix 是 "xml" 且 namespace 不是 XML 命名空间,则 抛出一个 "NamespaceError" DOMException

  10. 如果 qualifiedNameprefix 是 "xmlns" 并且 namespace 不是 XMLNS 命名空间,则 抛出一个 "NamespaceError" DOMException

  11. 如果 namespaceXMLNS 命名空间 并且 qualifiedNameprefix 均不是 "xmlns",则 抛出一个 "NamespaceError" DOMException

  12. 返回 (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; // legacy
  readonly 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;  // legacy
  undefined 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]

一个事件有一个关联的触摸目标列表(一个列表,包含零个或多个潜在事件目标)。除非另有说明,否则它为空列表。