文档对象模型或“ DOM”是网页的接口。它实质上是页面的 API,允许程序读取和操纵页面的内容,结构和样式。让我们分解一下。
如何创建一个网页?
浏览器如何从源HTML文档转换为在视口中显示样式化的交互式页面的方式称为“关键渲染路径”。这个过程大致可以分为两个阶段。第一阶段涉及浏览器解析文档以确定最终将在页面上呈现什么,而第二阶段涉及浏览器执行呈现。

- CSSOM,表示与元素关联的样式
- DOM,元素的表示
DOM是如何创建的(及其外观如何)?
DOM 是源 HTML 文档的基于对象的表示形式。正如我们将在下面看到的那样,它具有一些差异,但是本质上是将HTML文档的结构和内容转换为可以由各种程序使用的对象模型的尝试。
DOM 的对象结构由所谓的“节点树”表示。之所以这样称呼它,是因为它可以被认为是一棵带有单个父干的树,该母干分支成几个子分支,每个子分支都可能有叶子。在这种情况下,父级“ stem”是根元素,子级“ branch”是嵌套元素,而“ leaves”是元素内的内容。
让我们以这个HTML文档为例:
<!doctype html>
<html lang="en">
<head>
<title>My first web page</title>
</head>
<body>
<h1>Hello, world!</h1>
<p>How are you?</p>
</body>
</html>
该文档可以表示为以下节点树:

DOM 不是什么?
在我上面给出的示例中,DOM似乎是源HTML文档或您看到的DevTools的一对一映射。但是,正如我提到的,存在差异。为了完全理解DOM 是什么,我们需要看一下它不是什么。
DOM 不是您的源 HTML
尽管DOM是从源HTML文档创建的,但它并不总是完全相同。在两种情况下,DOM 可能与源 HTML 不同。
- HTML 无效时
DOM 是有效 HTML文档的接口。在创建DOM的过程中,浏览器可能会更正HTML代码中的某些无效内容。 让我们以这个HTML文档为例:
<!doctype html>
<html>
Hello, world!
</html>
该文档缺少<head> and <body>元素,这是有效 HTML 的要求。如果查看生成的 DOM 树,我们将看到已被纠正:

- 使用Javascript 修改 DOM 时
除了作为查看HTML文档内容的接口之外,还可以修改DOM,使其成为一种活跃的资源。
例如,我们可以使用Javascript为DOM创建其他节点。
var newParagraph = document.createElement("p");
var paragraphContent = document.createTextNode("I'm new!");
newParagraph.appendChild(paragraphContent);
document.body.appendChild(newParagraph);
这将更新 DOM,但当然不会更新我们的 HTML 文件。
DOM 不是您在浏览器中看到的(即渲染树)
在浏览器视口中看到的是渲染树,正如我提到的,它是 DOM 和 CSSOM 的组合。真正将 DOM 与渲染树区分开的是,后者仅由最终将在屏幕上绘制的内容组成。
由于渲染树仅与渲染的内容有关,因此它排除了视觉上隐藏的元素。例如,具有 display: none 与之关联的样式的元素。
<!doctype html>
<html lang="en">
<head></head>
<body>
<h1>Hello, world!</h1>
<p style="display: none;">How are you?</p>
</body>
</html>
DOM 将包含以下 <p> 元素:

但是,渲染树(因此在视口中看到的树)将不包含该元素。

DOM 不是 DevTools中的
这种差异要小得多,因为 DevTools 元素检查器提供了与浏览器中的 DOM 最接近的近似值。但是,DevTools 检查器包含 DOM 中没有的其他信息。
最好的例子是 CSS伪元素。使用::before和::after选择器创建的伪元素构成 CSSOM 和渲染树的一部分,但从技术上讲不是DOM的一部分。这是因为 DOM 是仅从源 HTML 文档构建的,不包括应用于元素的样式。
尽管伪元素不是 DOM 的一部分,但它们在我们的 devtools 元素检查器中。

总结
DOM 是 HTML 文档的接口。浏览器将其用作确定在视口中呈现内容的第一步,而 Javascript 程序将其用于修改页面的内容,结构或样式。 尽管与源 HTML 文档的其他形式相似,但 DOM 在许多方面有所不同:
- 它始终是有效的 HTML
- 这是一种可以通过 Javascript 修改的活动模型
- 它不包含伪元素(例如::after)
- 它确实包含隐藏的元素(例如,带有display: none)