HTML 文档的主干是标签(tag)。
根据文档对象模型(DOM),每个 HTML 标签都是一个对象。嵌套的标签是闭合标签的“子标签(children)”。标签内的文本也是一个对象。
所有这些对象都可以通过 JavaScript 来访问,我们可以使用它们来修改页面。
例如,document.body 是表示 标签的对象。
运行这段代码会使 保持 3 秒红色状态:
document.body.style.background = 'red'; // 将背景设置为红色
setTimeout(() => document.body.style.background = '', 3000); // 恢复回去
在这,我们使用了 style.background 来修改 document.body 的背景颜色,但是还有很多其他的属性,例如:
- innerHTML — 节点的 HTML 内容。
- offsetWidth — 节点宽度(以像素度量)
- ……等。
很快,我们将学习更多操作 DOM 的方法,但首先我们需要了解 DOM 的结构。
让我们从下面这个简单的文档(document)开始:
<!DOCTYPE HTML>
<html>
<head>
<title>About elk</title>
</head>
<body>
The truth about elk.
</body>
</html>
DOM 将 HTML 表示为标签的树形结构。它看起来如下所示:
每个树的节点都是一个对象。
标签被称为 元素节点(或者仅仅是元素),并形成了树状结构: 在根节点, 和 是其子项,等。
元素内的文本形成 文本节点,被标记为 #text。一个文本节点只包含一个字符串。它没有子项,并且总是树的叶子。
例如,
标签里面有文本 “About elk”。<br />请注意文本节点中的特殊字符:</p>
<ul>
<li>换行符:↵(在 JavaScript 中为 \n)</li><li>空格:␣</li></ul>
<p>空格和换行符都是完全有效的字符,就像字母和数字。它们形成文本节点并成为 DOM 的一部分。所以,例如,在上面的示例中,<head> 标签中的 <title> 标签前面包含了一些空格,并且该文本变成了一个 #text 节点(它只包含一个换行符和一些空格)。</p>
<p>只有两个顶级排除项:</p>
<ol>
<li>由于历史原因,<head> 之前的空格和换行符均被忽略。</li><li>如果我们在 </body> 之后放置一些东西,那么它会被自动移动到 body 内,并处于 body 中的最下方,因为 HTML 规范要求所有内容必须位于 <body> 内。所以 </body> 之后不能有空格。</li></ol>
<p>在其他情况下,一切都很简单 — 如果文档中有空格(就像任何字符一样),那么它们将成为 DOM 中的文本节点,而如果我们删除它们,则不会有任何空格。<br />这是没有空格的文本节点:</p>
<pre><code class="lang-javascript"><!DOCTYPE HTML>
<html><head><title>About elk</title></head><body>The truth about elk.</body></html>
</code></pre>
<p><img src="https://cdn.nlark.com/yuque/0/2021/png/2384107/1627032249689-fd058426-2eb8-42a7-ba3a-021ce634f0f6.png#clientId=u9574c30a-c0c1-4&from=paste&height=194&id=u03537950&margin=%5Bobject%20Object%5D&name=image.png&originHeight=194&originWidth=378&originalType=binary&ratio=1&size=6654&status=done&style=none&taskId=udbb8ab3a-c6be-4583-b894-4e8d5c1d655&width=378" alt="image.png"></p>
<blockquote>
<p><strong>字符串开头/结尾处的空格,以及只有空格的文本节点,通常会被工具隐藏</strong>
与 DOM 一起使用的浏览器工具(即将介绍)通常不会在文本的开始/结尾显示空格,并且在标签之间也不会显示空文本节点(换行符)。
开发者工具通过这种方式节省屏幕空间。
在本教程中,如果这些空格和空文本节点无关紧要时,我们在后面出现的关于 DOM 的示意图中会忽略它们。这样的空格通常不会影响文档的显示方式。</p>
</blockquote>
<p><a name="ZOukR"></a></p>
<h2 id="3b3vj"><a name="3b3vj" class="reference-link"></a><span class="header-link octicon octicon-link"></span><a rel="nofollow" href="https://zh.javascript.info/dom-nodes#zi-dong-xiu-zheng">自动修正</a></h2><p>如果浏览器遇到格式不正确的 HTML,它会在形成 DOM 时自动更正它。</p>
<p>例如,顶级标签总是 <html>。即使它不存在于文档中 — 它也会出现在 DOM 中,因为浏览器会创建它。对于 <body> 也是一样。</p>
<p>例如,如果一个 HTML 文件中只有一个单词 “Hello”,浏览器则会把它包装到 <html> 和 <body> 中,并且会添加所需的 <head>,DOM 将会变成下面这样:<br /><img src="https://cdn.nlark.com/yuque/0/2021/png/2384107/1627032805028-74408b76-8bc2-44eb-ae0a-3d156a876c9b.png#clientId=u9574c30a-c0c1-4&from=ui&id=u9e0ea075&margin=%5Bobject%20Object%5D&name=%E8%87%AA%E5%8A%A8%E4%BF%AE%E6%AD%A3.png&originHeight=130&originWidth=366&originalType=binary&ratio=1&size=3338&status=done&style=none&taskId=ufcfc3079-be78-4a34-a4c6-5e738499e11" alt="自动修正.png"><br />在生成 DOM 时,浏览器会自动处理文档中的错误,关闭标签等。<br />一个没有关闭标签的文档:</p>
<pre><code class="lang-javascript"><p>Hello
<li>Mom
<li>and
<li>Dad
</code></pre>
<p>……将成为一个正常的 DOM,因为浏览器在读取标签时会填补缺失的部分:<br /><img src="https://cdn.nlark.com/yuque/0/2021/png/2384107/1627032938179-ea226fb4-639b-442a-919f-7c08674b4a5e.png#clientId=u9574c30a-c0c1-4&from=ui&id=u3cf0c1ca&margin=%5Bobject%20Object%5D&name=%E8%87%AA%E5%8A%A8%E4%BF%AE%E6%AD%A32.png&originHeight=342&originWidth=373&originalType=binary&ratio=1&size=8427&status=done&style=none&taskId=u5a75c9d8-0d2e-4f3b-91f7-cd5c3fd7fc7" alt="自动修正2.png"></p>
<blockquote>
<p><strong>表格永远有 <tbody></strong>
表格是一个有趣的“特殊的例子”。按照 DOM 规范,它们必须具有 <tbody>,但 HTML 文本却(官方的)忽略了它。然后浏览器在创建 DOM 时,自动地创建了 <tbody>。
对于 HTML:</p>
</blockquote>
<pre><code class="lang-javascript"><table id="table"><tr><td>1</td></tr></table>
</code></pre>
<blockquote>
<p>DOM 结构会变成:</p>
</blockquote>
<p><img src="https://cdn.nlark.com/yuque/0/2021/png/2384107/1627033032965-9a881009-827b-4c5a-b4c1-3935b8d18c3a.png#clientId=u9574c30a-c0c1-4&from=ui&id=u1a6f2ef2&margin=%5Bobject%20Object%5D&name=tbody.png&originHeight=165&originWidth=369&originalType=binary&ratio=1&size=3572&status=done&style=none&taskId=u41f18b23-21cb-4885-ab12-684c9a45df7" alt="tbody.png"><br />看到了吗?<tbody> 出现了。你应该记住这一点,以免在使用表格时,对这种情况感到惊讶。
<a name="Iv4hc"></a></p>
<h2 id="bg249"><a name="bg249" class="reference-link"></a><span class="header-link octicon octicon-link"></span><a rel="nofollow" href="https://zh.javascript.info/dom-nodes#qi-ta-jie-dian-lei-xing">其他节点类型</a></h2><p>除了元素和文本节点外,还有一些其他的节点类型。<br />例如,注释:</p>
<pre><code class="lang-javascript"><!DOCTYPE HTML>
<html>
<body>
The truth about elk.
<ol>
<li>An elk is a smart</li>
<!-- comment -->
<li>...and cunning animal!</li>
</ol>
</body>
</html>
</code></pre>
<p><img src="https://cdn.nlark.com/yuque/0/2021/png/2384107/1627033125638-7c386222-572f-4828-a95c-9374a90e70cb.png#clientId=u9574c30a-c0c1-4&from=ui&id=u1effe45b&margin=%5Bobject%20Object%5D&name=%E8%8A%82%E7%82%B9.png&originHeight=460&originWidth=367&originalType=binary&ratio=1&size=14376&status=done&style=none&taskId=u495c73bc-921f-4a21-8b54-b6be0c6d669" alt="节点.png"><br />在这里我们可以看到一个新的树节点类型 — <em>comment node</em>,被标记为 #comment,它在两个文本节点之间。<br />我们可能会想 — 为什么要将注释添加到 DOM 中?它不会对视觉展现产生任何影响吗。但是有一条规则 — 如果一些内容存在于 HTML 中,那么它也必须在 DOM 树中。</p>
<p><strong>HTML 中的所有内容,甚至注释,都会成为 DOM 的一部分。</strong><br />甚至 HTML 开头的 <!DOCTYPE…> 指令也是一个 DOM 节点。它在 DOM 树中位于 <html> 之前。我们不会触及那个节点,出于这个原因,我们甚至不会在图表中绘制它,但它确实就在那里。<br />表示整个文档的 document 对象,在形式上也是一个 DOM 节点。</p>
<p>一共有 <a rel="nofollow" href="https://dom.spec.whatwg.org/#node">12 种节点类型</a>。实际上,我们通常用到的是其中的 4 种:</p>
<ol>
<li>document — DOM 的“入口点”。</li><li>元素节点 — HTML 标签,树构建块。</li><li>文本节点 — 包含文本。</li><li>注释 — 有时我们可以将一些信息放入其中,它不会显示,但 JS 可以从 DOM 中读取它。
<a name="NLZBC"></a><h2 id="bwnryp"><a name="bwnryp" class="reference-link"></a><span class="header-link octicon octicon-link"></span><a rel="nofollow" href="https://zh.javascript.info/dom-nodes#zi-ji-kan-kan">自己看看</a></h2>要在实际中查看 DOM 结构,请尝试 <a rel="nofollow" href="http://software.hixie.ch/utilities/js/live-dom-viewer/">Live DOM Viewer</a>。只需输入文档,它将立即显示为 DOM。<br />探索 DOM 的另一种方式是使用浏览器开发工具。实际上,这就是我们在开发中所使用的。<br />你可以打开这个网页 <a rel="nofollow" href="https://zh.javascript.info/article/dom-nodes/elks.html">elks.html</a>,然后打开浏览器开发工具,并切换到元素(Elements)选项卡。<br />它看起来像这样:<br /><img src="https://cdn.nlark.com/yuque/0/2021/png/2384107/1627033266769-f496807c-b426-4e1c-9d7d-61fbfb29d885.png#clientId=u9574c30a-c0c1-4&from=ui&id=uf946fa74&margin=%5Bobject%20Object%5D&name=%E4%B8%8B%E8%BD%BD.png&originHeight=498&originWidth=1396&originalType=binary&ratio=1&size=115512&status=done&style=none&taskId=u293f1e06-d87c-415c-97ca-e7518b6740c" alt="下载.png"><br />你可以看到 DOM,点击元素,查看它们的细节等。</li></ol>
<p>请注意,开发者工具中的 DOM 结构是经过简化的。文本节点仅以文本形式显示。并且根本没有“空白”(只有空格)的文本节点。这其实挺好,因为大多数情况下,我们只关心元素节点。</p>
<p>点击左上角的 按钮可以让我们使用鼠标(或其他指针设备)从网页中选择一个节点并“检查(inspect)”它(在元素选项卡中滚动到该节点)。当我们有一个巨大的 HTML 页面(和相应的巨大 DOM),并希望查看其中的一个特定元素的位置时,这很有用。</p>
<p>另一种方法是在网页上右键单击,然后在上下文菜单中选择“检查(Inspect)”。<br /><img src="https://cdn.nlark.com/yuque/0/2021/png/2384107/1627033426640-9d8835e9-4037-42b0-9964-e03a1c35a4cc.png#clientId=u9574c30a-c0c1-4&from=ui&id=ud519a970&margin=%5Bobject%20Object%5D&name=%E4%B8%8B%E8%BD%BD%20%281%29.png&originHeight=758&originWidth=1392&originalType=binary&ratio=1&size=132976&status=done&style=none&taskId=uf89972fa-3513-4786-972a-083116cf8c7" alt="下载 (1).png">
<a name="OzY6U"></a></p>
<h2 id="bgr6pw"><a name="bgr6pw" class="reference-link"></a><span class="header-link octicon octicon-link"></span><a rel="nofollow" href="https://zh.javascript.info/dom-nodes#yu-kong-zhi-tai-jiao-hu">与控制台交互</a></h2><p>在我们处理 DOM 时,我们可能还希望对其应用 JavaScript。例如:获取一个节点并运行一些代码来修改它,以查看结果。以下是在元素(Elements)选项卡和控制台(Console)之间切换的一些技巧。<br />首先:</p>
<ul>
<li>在元素(Elements)选项卡中选择第一个 <li>。</li><li>按下 Esc — 它将在元素(Elements)选项卡下方打开控制台(Console)。</li></ul>
<p>现在最后选中的元素可以通过 $0 来进行操作,先前选择的是 $1,等。<br />我们可以对它们执行一些命令。例如,$0.style.background = ‘red’ 使选定的列表项(list item)变成红色,像这样:<br /><img src="https://cdn.nlark.com/yuque/0/2021/png/2384107/1627293068837-e2e69aa2-fa52-48f3-9312-accd8d7f53a2.png#clientId=uf3f5c162-4a14-4&from=ui&id=u4ce624fc&margin=%5Bobject%20Object%5D&name=%E4%B8%8B%E8%BD%BD%20%282%29.png&originHeight=900&originWidth=1398&originalType=binary&ratio=1&size=142820&status=done&style=none&taskId=u8817cc0f-8ab8-4b6e-b470-884e5c43bfa" alt="下载 (2).png"><br />这就是在控制台(Console)中获取元素(Elements)选项卡中的节点的方法。<br />还有一种方式。如果存在引用 DOM 节点的变量,那么我们可以在控制台(Console)中使用命令 inspect(node),来在元素(Elements)选项卡中查看它。<br />或者我们可以直接在控制台(Console)中输出 DOM 节点,并“就地”探索它,例如下面的 document.body:<br /><img src="https://cdn.nlark.com/yuque/0/2021/png/2384107/1627293119184-71c1f052-a0f9-440a-a22d-6f258d9d2fbe.png#clientId=uf3f5c162-4a14-4&from=ui&id=u775ee852&margin=%5Bobject%20Object%5D&name=%E4%B8%8B%E8%BD%BD%20%283%29.png&originHeight=1024&originWidth=1392&originalType=binary&ratio=1&size=151480&status=done&style=none&taskId=u1da61d95-30e1-421e-8802-76887568900" alt="下载 (3).png"><br />当然,这是出于调试目的。从下一章开始,我们将使用 JavaScript 访问和修改 DOM。<br />浏览器开发者工具对于开发有很大的帮助:我们可以探索 DOM,尝试一些东西,并找出问题所在。
<a name="INKUi"></a></p>
<h2 id="2r0br1"><a name="2r0br1" class="reference-link"></a><span class="header-link octicon octicon-link"></span><a rel="nofollow" href="https://zh.javascript.info/dom-nodes#zong-jie">总结</a></h2><p>HTML/XML 文档在浏览器内均被表示为 DOM 树。</p>
<ul>
<li>标签(tag)成为元素节点,并形成文档结构。</li><li>文本(text)成为文本节点。</li><li>……等,HTML 中的所有东西在 DOM 中都有它的位置,甚至对注释也是如此。</li></ul>
<p>我们可以使用开发者工具来检查(inspect)DOM 并手动修改它。<br />在这里,我们介绍了基础知识,入门最常用和最重要的行为。在 <a rel="nofollow" href="https://developers.google.cn/web/tools/chrome-devtools">https://developers.google.cn/web/tools/chrome-devtools</a> 上有关于 Chrome 开发者工具的详细文档说明。学习这些工具的最佳方式就是到处点一点看一看,阅读菜单:大多数选项都很明显。而后,当你大致了解它们后,请阅读文档并学习其余内容。</p>
<p>DOM 节点具有允许我们在它们之间移动,修改它们,在页面中移动等的属性和方法。在下一章中,我们将介绍它们。</p>