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
document是 DOM 树的根节点,也是 JS 访问 DOM 的入口html是 document 的唯一子元素(文档元素)head和body是 html 的两个子元素- 后面层层嵌套,形成完整的树
(3) 节点类型
DOM 中有三种核心节点类型:
| 节点类型 | 说明 | nodeType 值 |
|---|---|---|
| 元素节点 | HTML 标签,如 p、div |
1 |
| 文本节点 | 标签内的文字内容 | 3 |
| 属性节点 | 标签的属性,如 class、id |
2(已不推荐使用) |
HTML
<p class="intro">Hello World</p>
上面这一行中:<p> 是元素节点,class="intro" 是属性节点,Hello World 是文本节点。日常开发中,你操作最多的就是元素节点。
(4) document 对象
document 是 DOM 的入口,浏览器自动提供这个全局对象。通过它可以:
- 获取页面元素:
document.getElementById()、document.querySelector() - 创建新元素:
document.createElement() - 读写页面内容:
document.title、document.body - 监听页面事件:
document.addEventListener()
(5) DOM 的作用
JS 本身不能"看见"网页——它只能做计算。DOM 就是那双"眼睛"和"手":
- 读取:获取页面上的文字、属性、样式
- 修改:改变文字内容、属性值、CSS 样式
- 添加:创建新元素并插入页面
- 删除:移除页面上的元素
- 监听:响应用户的点击、输入等操作
没有 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"><' + 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 += '></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"></' +
node.nodeName.toLowerCase() + '></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>
▶ 示例: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>
▶ 示例:节点类型判断
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 = "<" + node.nodeName.toLowerCase() + ">";
} 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><p id="demo">这是一个<span>包含</span>多种节点的段落</p></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>
❓ 常见问题
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。
📖 小节
- DOM 是 HTML 文档的树形结构表示,每个 HTML 标签对应一个 DOM 节点
document对象是 DOM 的入口,document.getElementById()按 ID 查找元素document.querySelector()用 CSS 选择器查找第一个匹配元素,querySelectorAll找全部- 节点类型:元素节点(HTML 标签)、文本节点(文字内容)、属性节点(标签属性)
- DOM 树的核心操作:查找节点、读取属性、修改内容、添加/删除子节点
📝 作业
- 打开浏览器开发者工具(F12),在 Console 中输入
console.dir(document),观察 document 对象有哪些属性和方法,列出 5 个你认识或觉得有意思的。 - 写一段代码:获取当前页面的
body元素,打印它的childElementCount(子元素个数),然后用children遍历所有子元素,打印每个子元素的标签名。 - 解释"元素节点"和"文本节点"的区别。给定
<div>Hello <b>World</b></div>,画出它的节点树结构,标明每个节点的类型。