> For the complete documentation index, see [llms.txt](https://lizh.gitbook.io/knowledge/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://lizh.gitbook.io/knowledge/javascript-xi-lie/01-ji-chu-pian-09-wen-dang-dui-xiang-mo-xing-dom.md).

# 基础篇 09 文档对象模型（DOM）

文档对象模型（DOM，Document Object Model）是 HTML 和 XML 文档的编程接口。它提供了对文档的结构化的表述，并定义了一种方式可以使从程序中对该结构进行访问，从而改变文档的结构、样式和内容。DOM 将文档解析为一个由**节点和对象**（包含属性和方法的对象）组成的结构集合。简言之，它会将 Web 页面和脚本或程序语言连接起来。

一个 Web 页面是一个文档。这个文档可以在浏览器窗口或作为 HTML 源码显示出来。但上述两个情况中都是同一份文档。DOM 提供了对同一份文档的另一种表现、存储和操作的方式。DOM 是 Web 页面的完全的面向对象表述，它能够使用如 JavaScript 等脚本语言进行修改。

W3C DOM 和 WHATWG DOM 标准在绝大多数现代浏览器中都有对 DOM 的基本实现。**许多浏览器提供了对 W3C 标准的扩展**，所以在使用时必须注意，文档可能会在多种浏览器上使用不同的 DOM 来访问。

## DOM 中的接口

DOM 中的许多对象都实现了多个接口。

例如，Table 对象实现了 HTMLTableElement 接口，其中包括 createCaption 和 insertRow 方法；但由于 table 对象也是一个 HTML 元素，因此也实现了 Element 接口；而 HTML 元素对 DOM 来说也是组成 Web 页面或 XML 页面节点树中的一个节点，因此 table 对象也实现更基本的 Node 接口（Element 对象也继承这个接口）。

DOM 常见的接口有：

* **Window 接口：** 表示一个包含 DOM 文档的窗口。
* **Document 接口：** 表示任何在浏览器中载入的网页，并作为网页内容的入口，也就是 DOM 树。
* **Element 接口：** 描述所有元素普遍具有的方法和属性，Document 对象下的对象都继承自它。
* **Node 接口：** 表示 DOM 模型中的节点。
* **Attr 接口：** 表示一个 DOM 元素的属性。
* **Event 接口：** 表示在 DOM 中出现的事件。
* **MutationObserver 接口：** 提供了监视对 DOM 树所做更改的能力。
* **DocumentFragment 接口：** 表示一个没有父对象的最小文档对象。与 document 相比，最大的区别是 DocumentFragment 不是真实 DOM 树的一部分，它的变化不会触发 DOM 树的重新渲染，且不会导致性能等问题。
* **NodeList 接口：** 表示 DOM 模型中的节点集合。
* **EventTarget 接口：** 表示可以接收事件、并且可以创建侦听器的对象。

## Node 接口

Node 是一个接口，各种类型的 DOM API 对象会从这个接口继承。

以下接口都从 Node 继承其方法和属性：

```shell
Document
Element
Attr
CharacterData (被 Text、Comment、CDATASection 继承)
DocumentFragment
DocumentType、
...
```

### Node属性

从其父类型 EventTarget 继承属性。

#### baseURI（只读）

返回一个节点所在文档的绝对基址 URL；如果无法获取则可能返回 null 。

当浏览器要获取绝对 URL 时，就需要用基 URL 去解析相对 URL。如，img 元素的 src 属性是相对路径时。

一般情况下，基 URL 是文档的地址（浏览器显示的地址，可以通过 window\.location 获取），但是它受诸多方面因素的影响：

* HTML 的 base 元素。
* XML 的 xml:base 属性。

#### childNodes（只读）

返回包含指定节点的子节点的集合，该集合为即时更新的集合。

#### firstChild（只读）

返回指定节点的第一个子节点，如果节点是无子节点，则返回 null。

#### lastChild（只读）

返回指定节点的最后一个子节点，如果该节点没有子节点则返回 null。

#### isConnected（只读）

返回一个布尔值。如果指定节点与 DOM 树连接则返回 true, 否则返回 false。

#### nextSibling（只读）

返回其父节点的子节点列表中紧跟在其后面的节点，如果指定的节点为最后一个节点，则返回 null。

#### previousSibling（只读）

返回其父节点的子节点列表中的前一个节点，如果指定的节点为第一个节点，则返回 null。

> **注意：** Gecko 内核的浏览器会在源代码中标签内部有空白符的地方插入一个文本结点到文档中。因此，使用firstChild、previousSibling 等方法可能会返回一个空白符文本节点。

#### nodeName（只读）

返回该节点名字的字符串。节点的名字和节点类型可能不同，HTMLElement 节点的名字跟关联的标签对应（HTMLAudioElement 的就是 `audio`），而 Text 节点对应的是 `#text`，还有 Document 节点对应的是 `#document`。

#### nodeType（只读）

返回一个与该节点类型对应的无符号短整型的值，可能的值如下：

* ELEMENT\_NODE：1，元素节点。
* TEXT\_NODE： 3，Element 或者 Attr 中实际的文字。
* CDATA\_SECTION\_NODE： 4。
* PROCESSING\_INSTRUCTION\_NODE： 7，一个用于 XML 文档的 ProcessingInstruction。
* COMMENT\_NODE： 8，注释节点。
* DOCUMENT\_NODE： 9，Document 节点。
* DOCUMENT\_TYPE\_NODE： 10，描述文档类型的 DocumentType 节点。如，`<!DOCTYPE html>` 就是用于 HTML5 的。
* DOCUMENT\_FRAGMENT\_NODE： 11，DocumentFragment 节点。

#### nodeValue

返回或设置当前节点的值。

对于 Document、Element 节点来说，返回 null；对于 text、comment、CDATA 节点来说，返回该节点的文本内容；对于 attribute 节点来说，返回该属性的属性值。

#### ownerDocument（只读）

返回当前节点的顶层的 Document 对象。

#### parentNode（只读）

返回一个当前节点的父节点。 如果当前节点没有父节点或者说父节点不是一个元素，返回 null。

一个元素节点的父节点可能是一个元素节点，也可能是一个文档节点，或者是个文档碎片节点。

#### parentElement（只读）

返回当前节点的父元素节点，如果该元素没有父节点，或者父节点不是一个 DOM 元素，则返回 null。

#### textContent

返回或设置一个元素内所有子节点及其后代的文本内容。

### Node方法

从其父类型 EventTarget 继承方法。

#### appendChild()

将指定的节点作为最后一个子节点添加到当前节点。如果指定的节点是 DOM 树上的现有节点，则节点将从当前位置分离，并附加到新位置。

#### cloneNode()

复制一个节点，并且可以选择是否克隆这个节点下的所有内容。

**备注：** 在 DOM4 规范中，默认值为 true，即节点下的内容会被克隆。但，在最新的规范里，其默认值变成了 false。

**备注：** 克隆一个元素节点会拷贝它所有的属性以及属性值，当然也就包括了属性上绑定的事件（比如，`onclick="alert(1)"`），但不会拷贝那些使用 addEventListener() 方法或者 `node.onclick = fn` 用动态绑定的事件。

#### compareDocumentPosition()

比较当前节点与任意文档中的另一个节点的位置关系。

```javascript
node.compareDocumentPosition( otherNode )
// node，当前节点
// otherNode，指定节点
```

* DOCUMENT\_POSITION\_DISCONNECTED： 1，不在同一文档中。
* DOCUMENT\_POSITION\_PRECEDING： 2，指定节点在当前节点之前。
* DOCUMENT\_POSITION\_FOLLOWING： 4，指定节点在当前节点之后。
* DOCUMENT\_POSITION\_CONTAINS： 8，指定节点包含当前节点。
* DOCUMENT\_POSITION\_CONTAINED\_BY： 16，指定节点被当前节点包含。
* DOCUMENT\_POSITION\_IMPLEMENTATION\_SPECIFIC： 32，待定。

#### contains()

返回的是一个布尔值，来表示传入的节点是否为该节点的后代节点。

#### getRootNode()

返回上下文对象的根节点。如果 shadow root 节点存在的话，也可以在返回的节点中包含它。

#### hasChildNodes()

返回一个布尔值，来表示当前节点是否包含有子节点。

#### insertBefore()

在当前节点内，插入一个子节点，并使该子节点位于参考节点的前面。如果参考节点为 null，则会被插入到父元素中，位于最后一个节点后面。

```javascript
parentNode.insertBefore(newNode, referenceNode);
```

#### isDefaultNamespace()

返回一个布尔值。接受一个命名空间 URI 作为参数，当参数所指代的命名空间是默认命名空间时返回 true，否则返回 false。

#### isEqualNode()

返回一个布尔值，表示两个节点是否相等。当两个节点的类型相同，定义特征 相同（对元素来说，即 id、孩子节点的数量等等）、属性一致等，这两个节点就是相等的。

一些具体的数据指出：多数时候的比较是根据节点的类型来的。

#### lookupPrefix()

返回包含参数 URI 所对应的命名空间前缀的字符串，若不存在则返回 null。如果存在多个可匹配的前缀，则返回结果和浏览器具体实现有关。

#### lookupNamespaceURI()

接受一个前缀，并返回前缀所对应节点命名空间 URI 。如果 URI 不存在则返回 null。传入 null 作为 prefix 参数将返回默认命名空间。

#### normalize()

对节点下的所有文本子节点进行整理，合并相邻的文本节点并清除**空文本节点**。

* **空文本节点** 并不包括空白字符（空格、换行等）构成的文本节点。
* 两个以上相邻文本节点的产生原因包括：通过脚本调用有关的 DOM 接口进行了文本节点的插入和分割等；HTML 中超长的文本节点会被浏览器自动分割为多个相邻文本节点。

#### removeChild()

移除当前节点的一个子节点，返回删除的子节点。这个子节点必须存在于当前节点中。

#### replaceChild()

用指定的节点替换当前节点的一个子节点，并返回被替换掉的节点。

## Attr 接口

Attr 接口表示一个 DOM 元素的属性。

在大多数 DOM 方法中，可以直接通过字符串的方式获取属性值（例如，Element.getAttribute()），但是一些方法（Element.getAttributeNode()）、或属性（Element.attributes）、或通过迭代器访问时，则返回 Attr 对象。

### Attr 属性

#### name（只读）

返回属性的名称。如果有命名空间前缀，则前缀在属性名称前面。如，属性名称是 `lang`，命名空间前缀是 `xml`，则返回的限定名称是 `xml:lang`。

#### namespaceURI（只读）

返回属性的命名空间 URI。如果该元素不在命名空间中，则返回 null。

#### localName（只读）

返回一个属性限定名称的本名部分（去除命名空间前缀的名字）。

#### prefix（只读）

返回指定标签属性的名字空间前缀，如果没有前缀则返回 null。

#### ownerElement（只读）

返回所附属的元素节点。

**备注：** DOM Level 4 移除了这个方法，但最新的 DOM 草案再次引入该属性。

#### value

返回属性的值。

## 参考资料

[MDN - 文档对象模型 (DOM)](https://developer.mozilla.org/zh-CN/docs/Web/API/Document_Object_Model)

[MDN - DOM概述](https://developer.mozilla.org/zh-CN/docs/Web/API/Document_Object_Model/Introduction)

[MDN - Node接口](https://developer.mozilla.org/zh-CN/docs/Web/API/Node)

[MDN - Attr接口](https://developer.mozilla.org/zh-CN/docs/Web/API/Attr)
