JavaScript: DOM 简介

DOM 是 JavaScript 操作网页的唯一桥梁。没有 DOM,JS 就是一门纯计算语言;有了 DOM,JS 就能"看见"页面、修改页面、让页面活起来。

1. DOM 树与 document 对象

▶ 示例

JAVASCRIPT
// 本节代码示例
const message = "你好,世界!";
console.log(message);
▶ 试一试

这是最简单的示例,后续章节会详细解释。### (1) 什么是 DOM

DOM 全称 Document Object Model(文档对象模型)。浏览器加载 HTML 后,不是把 HTML 当纯文本存着,而是把它翻译成一棵对象树——每个 HTML 标签变成一个对象(叫节点),标签之间的嵌套关系变成树形的父子关系。

你可以把 DOM 想象成一颗倒着长的树:最上面是树根(document),往下分出枝干(html、head、body),再往下是更细的枝叶(div、p、span...)。

(2) DOM 树的结构

一个简单 HTML 页面的 DOM 树大致如下:

HTML
document
  └── html
       ├── head
       │    ├── meta
       │    └── title
       └── body
            ├── h1
            ├── p
            └── div
                 └── span

(3) 节点类型

DOM 中有三种核心节点类型:

节点类型 说明 nodeType 值
元素节点 HTML 标签,如 pdiv 1
文本节点 标签内的文字内容 3
属性节点 标签的属性,如 classid 2(已不推荐使用)
HTML
<p class="intro">Hello World</p>

上面这一行中:<p> 是元素节点,class="intro" 是属性节点,Hello World 是文本节点。日常开发中,你操作最多的就是元素节点。

(4) document 对象

document 是 DOM 的入口,浏览器自动提供这个全局对象。通过它可以:

(5) DOM 的作用

JS 本身不能"看见"网页——它只能做计算。DOM 就是那双"眼睛"和"手":

没有 DOM,JS 和 HTML 就是两个互不相干的东西。DOM 把它们连在了一起。


▶ 示例:查看 DOM 结构

HTML 📖 仅展示
<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
  <title>DOM结构</title>
  <style>
    body { font-family: sans-serif; padding: 20px; }
    .tree { margin: 16px 0; padding: 16px; background: #f5f5f5; border-radius: 8px; font-family: monospace; font-size: 14px; line-height: 1.8; }
    .node { color: #4a90d9; font-weight: bold; }
    .text-node { color: #5cb85c; font-style: italic; }
    .attr-node { color: #f0ad4e; }
    .indent { margin-left: 24px; }
    .info { background: #e8f4fd; padding: 12px; border-radius: 6px; margin: 16px 0; }
  </style>
</head>
<body>
  <h1 id="mainTitle">DOM 结构演示</h1>
  <p class="intro">这是一个段落</p>
  <div id="container">
    <span>内联元素</span>
  </div>
  <div id="output"></div>
  <script>
    function buildTree(node, depth) {
      if (depth > 4) return "";
      var indent = "";
      for (var i = 0; i < depth; i++) indent += "  ";
      var result = "";

      if (node.nodeType === 1) {
        result += indent + '<span class="node">&lt;' + node.nodeName.toLowerCase();
        if (node.attributes && node.attributes.length > 0) {
          for (var a = 0; a < node.attributes.length && a < 3; a++) {
            result += ' <span class="attr-node">' +
              node.attributes[a].name + '="' + node.attributes[a].value + '"</span>';
          }
        }
        result += '&gt;</span>\n';
      } else if (node.nodeType === 3) {
        var text = node.textContent.trim();
        if (text) {
          result += indent + '<span class="text-node">"' + text + '"</span>\n';
        }
        return result;
      }

      var children = node.childNodes;
      for (var c = 0; c < children.length; c++) {
        result += buildTree(children[c], depth + 1);
      }

      if (node.nodeType === 1) {
        result += indent + '<span class="node">&lt;/' +
          node.nodeName.toLowerCase() + '&gt;</span>\n';
      }

      return result;
    }

    var treeStr = buildTree(document.documentElement, 0);

    document.getElementById("output").innerHTML = `
      <h2>当前页面的 DOM 树</h2>
      <div class="tree"><pre>${treeStr}</pre></div>
      <div class="info">
        <strong>颜色说明:</strong>
        <span class="node">蓝色 = 元素节点</span> |
        <span class="text-node">绿色 = 文本节点</span> |
        <span class="attr-node">橙色 = 属性</span>
      </div>
    `;
  </script>
</body>
</html>
逻辑代码 68 行(超过 40 行限制,仅展示)

▶ 示例:document 对象探索

HTML 📖 仅展示
<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
  <title>document对象</title>
  <style>
    body { font-family: sans-serif; padding: 20px; }
    .card { border: 2px solid #4a90d9; border-radius: 8px; padding: 16px; margin: 12px 0; background: #f0f7ff; }
    .card h3 { margin: 0 0 8px; color: #4a90d9; }
    .prop { display: flex; justify-content: space-between; padding: 6px 0; border-bottom: 1px solid #e0e0e0; }
    .prop:last-child { border-bottom: none; }
    .key { font-weight: bold; color: #333; }
    .val { color: #4a90d9; font-family: monospace; word-break: break-all; }
    button { padding: 10px 20px; font-size: 16px; border: none; border-radius: 6px; cursor: pointer; margin: 4px; }
    .btn-blue { background: #4a90d9; color: #fff; }
    .btn-blue:hover { background: #357abd; }
    .btn-green { background: #5cb85c; color: #fff; }
    .btn-green:hover { background: #449d44; }
    .btn-orange { background: #f0ad4e; color: #fff; }
    .btn-orange:hover { background: #ec971f; }
    .demo-area { min-height: 60px; border: 2px dashed #ccc; border-radius: 8px; padding: 16px; margin: 12px 0; text-align: center; }
  </style>
</head>
<body>
  <h2>document 对象探索</h2>

  <div class="card">
    <h3>document 常用属性</h3>
    <div id="props"></div>
  </div>

  <div class="card">
    <h3>动手试一试</h3>
    <button class="btn-blue" id="btnTitle">修改 document.title</button>
    <button class="btn-green" id="btnBody">改变 body 背景</button>
    <button class="btn-orange" id="btnCreate">创建新元素</button>
    <button class="btn-blue" id="btnReset" style="background:#888;">重置</button>
  </div>

  <div class="demo-area" id="demoArea">
    <p>这里是演示区域</p>
  </div>

  <script>
    function showProps() {
      var props = [
        ["document.title", document.title],
        ["document.URL", document.URL],
        ["document.domain", document.domain],
        ["document.contentType", document.contentType],
        ["document.documentElement", document.documentElement.nodeName],
        ["document.body", document.body.nodeName],
        ["document.head", document.head.nodeName],
        ["document.body.childElementCount", document.body.childElementCount],
        ["document.all.length", document.all.length + " 个元素"]
      ];

      document.getElementById("props").innerHTML = props.map(function(p) {
        return '<div class="prop"><span class="key">' + p[0] +
          '</span><span class="val">' + p[1] + '</span></div>';
      }).join("");
    }

    showProps();

    var titleCount = 0;
    document.getElementById("btnTitle").addEventListener("click", function() {
      titleCount++;
      document.title = "修改第 " + titleCount + " 次!";
      showProps();
    });

    var bgColors = ["#fff8e1", "#e8f5e9", "#fce4ec", "#e3f2fd", "#f3e5f5"];
    var bgIndex = 0;
    document.getElementById("btnBody").addEventListener("click", function() {
      document.body.style.background = bgColors[bgIndex % bgColors.length];
      bgIndex++;
    });

    var createCount = 0;
    document.getElementById("btnCreate").addEventListener("click", function() {
      createCount++;
      var p = document.createElement("p");
      p.textContent = "新创建的段落 #" + createCount;
      p.style.color = "#4a90d9";
      p.style.fontWeight = "bold";
      document.getElementById("demoArea").appendChild(p);
      showProps();
    });

    document.getElementById("btnReset").addEventListener("click", function() {
      document.title = "document对象";
      document.body.style.background = "";
      document.getElementById("demoArea").innerHTML = "<p>这里是演示区域</p>";
      titleCount = 0;
      bgIndex = 0;
      createCount = 0;
      showProps();
    });
  </script>
</body>
</html>
逻辑代码 92 行(超过 40 行限制,仅展示)

▶ 示例:节点类型判断

HTML 📖 仅展示
<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
  <title>节点类型</title>
  <style>
    body { font-family: sans-serif; padding: 20px; }
    table { border-collapse: collapse; margin: 16px 0; width: 100%; max-width: 700px; }
    td, th { border: 1px solid #ddd; padding: 10px 14px; text-align: left; }
    th { background: #4a90d9; color: #fff; }
    .elem { background: #e3f2fd; }
    .text { background: #e8f5e9; }
    .note { background: #fff8e1; padding: 12px; border-radius: 6px; border-left: 4px solid #f0ad4e; margin: 16px 0; max-width: 700px; }
  </style>
</head>
<body>
  <h2>节点类型详解</h2>
  <p id="demo">这是一个<span>包含</span>多种节点的段落</p>
  <div id="output"></div>
  <script>
    var demo = document.getElementById("demo");
    var results = [];

    function analyzeNode(node, depth) {
      var indent = "";
      for (var i = 0; i < depth; i++) indent += "│ ";
      var typeMap = { 1: "元素节点", 3: "文本节点" };
      var typeName = typeMap[node.nodeType] || "其他(" + node.nodeType + ")";
      var detail = "";

      if (node.nodeType === 1) {
        detail = "&lt;" + node.nodeName.toLowerCase() + "&gt;";
      } else if (node.nodeType === 3) {
        var text = node.textContent.trim();
        if (text) detail = '"' + text + '"';
        else return;
      }

      var cls = node.nodeType === 1 ? "elem" : "text";
      results.push(
        '<tr class="' + cls + '">' +
        "<td>" + indent + "</td>" +
        "<td><code>" + detail + "</code></td>" +
        "<td>" + typeName + "</td>" +
        "<td>" + node.nodeType + "</td>" +
        "</tr>"
      );

      var children = node.childNodes;
      for (var c = 0; c < children.length; c++) {
        analyzeNode(children[c], depth + 1);
      }
    }

    analyzeNode(demo, 0);

    document.getElementById("output").innerHTML = `
      <p>分析 <code>&lt;p id="demo"&gt;这是一个&lt;span&gt;包含&lt;/span&gt;多种节点的段落&lt;/p&gt;</code></p>
      <table>
        <tr><th>层级</th><th>内容</th><th>节点类型</th><th>nodeType</th></tr>
        ${results.join("")}
      </table>
      <div class="note">
        💡 注意:标签之间的换行和空格也是文本节点!实际开发中,<code>children</code>(只含元素节点)比
        <code>childNodes</code>(含所有节点)更好用。
      </div>
    `;
  </script>
</body>
</html>
逻辑代码 64 行(超过 40 行限制,仅展示)

❓ 常见问题

Q DOM 是 JavaScript 的一部分吗?
A 不是。DOM 是 W3C 制定的标准,JavaScript 只是提供了操作 DOM 的 API。Node.js 没有 DOM,因为它不是浏览器环境。把 DOM 和 JS 混为一谈是新手常见误区。
Q 为什么 childNodes 里会有空文本节点?
A HTML 中的换行和缩进在 DOM 中会被解析为文本节点。比如标签之间的换行就是一个文本节点(内容是换行符)。如果只想获取元素节点,用 children 代替 childNodes
Q 修改 DOM 会影响页面性能吗?
A 会。每次修改 DOM 都可能触发浏览器的重新布局(reflow)和重绘(repaint),频繁操作 DOM 是性能杀手。最佳实践是:先在内存中拼好所有修改,最后一次写入 DOM。

📖 小节

📝 作业

  1. 打开浏览器开发者工具(F12),在 Console 中输入 console.dir(document),观察 document 对象有哪些属性和方法,列出 5 个你认识或觉得有意思的。
  2. 写一段代码:获取当前页面的 body 元素,打印它的 childElementCount(子元素个数),然后用 children 遍历所有子元素,打印每个子元素的标签名。
  3. 解释"元素节点"和"文本节点"的区别。给定 <div>Hello <b>World</b></div>,画出它的节点树结构,标明每个节点的类型。
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏