XML DOM 节点树(图文最清楚版)
XML DOM 的核心思想就是:把整个 XML 文档在内存中表示成一棵“节点树”(Node Tree),
你可以像逛公园一样在这棵树上随意“走来走去、修枝剪叶”。
1. 节点树完整结构图(强烈建议看这个图)
拿下面这个 XML 为例:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE bookstore SYSTEM "bookstore.dtd">
<bookstore specialty="IT技术">
<!-- 热门图书区 -->
<book id="1" category="编程">
<title lang="zh">JavaScript 高级程序设计</title>
<author>扎卡斯</author>
<year>2020</year>
<price>99.00</price>
</book>
<book id="2" category="科幻">
<title lang="zh">三体</title>
<author>刘慈欣</author>
</book>
</bookstore>
这棵完整的节点树长这样(竖向展开):
文档节点 (Document) nodeType=9 #document
│
├── 处理指令节点 (xml声明) nodeType=7 target="xml"
├── 文档类型节点 (DOCTYPE) nodeType=10 bookstore
├── 注释节点 (<!-- 热门图书区 -->) nodeType=8
│
└── 元素节点 <bookstore> nodeType=1 bookstore
├── 属性节点 specialty="IT技术"
├── 属性节点 (没有 id)
│
├── 文本节点(换行+空格) nodeType=3 "#text"
├── 注释节点(<!-- 热门图书区 -->)
├── 文本节点(换行+空格)
│
└── 元素节点 <book> (第一本) nodeType=1 book
├── 属性节点 id="1"
├── 属性节点 category="编程"
│
├── 文本节点(换行+缩进)
│
├── 元素节点 <title> nodeType=1 title
│ ├── 属性节点 lang="zh"
│ └── 文本节点 "JavaScript 高级程序设计"
│
├── 文本节点(换行+缩进)
├── 元素节点 <author> "扎卡斯"
├── 文本节点(换行+缩进)
├── 元素节点 <year> "2020"
├── 文本节点(换行+缩进)
└── 元素节点 <price> "99.00"
│
├── 文本节点(换行)
│
└── 元素节点 <book> (第二本) nodeType=1 book
├── 属性节点 id="2"
├── 属性节点 category="科幻"
├── 文本节点(换行+缩进)
├── 元素节点 <title lang="zh"> "三体"
└── 元素节点 <author> "刘慈欣"
2. 节点树中的“家族关系”口诀(背会就无敌)
| 关系 | 属性名 | 说明 | 例子 |
|---|---|---|---|
| 爸爸 | parentNode | 永远只有一个(文档节点除外) | book.parentNode → bookstore |
| 儿子们 | childNodes | 包含所有子节点(包括文本、注释) | bookstore.childNodes |
| 只认亲儿子 | children | 只包含元素子节点(不含文本、注释) | bookstore.children |
| 大儿子 | firstChild | 可能是空白文本! | bookstore.firstChild → 换行文本 |
| 小儿子 | lastChild | 同上 | |
| 大哥 | previousSibling | 上一个兄弟(可能也是文本) | 第二个book.previousSibling → 换行 |
| 弟弟 | nextSibling | 下一个兄弟 | |
| 第一个亲儿子 | firstElementChild | 跳过空白文本,直接拿到第一个元素子节点 | bookstore.firstElementChild → book |
| 最后一个亲儿子 | lastElementChild | 同上 |
现代浏览器推荐使用下面这四个(自动跳过空白文本):
parent.firstElementChild
parent.lastElementChild
node.previousElementSibling
node.nextElementSibling
3. 实战遍历整棵树(三种经典写法)
const doc = new DOMParser().parseFromString(xmlStr, "text/xml");
const root = doc.documentElement; // <bookstore>
// 写法1:递归遍历(最常用)
function walk(node, depth = 0) {
const indent = " ".repeat(depth);
console.log(indent + node.nodeName +
(node.nodeType === 3 ? `="${node.nodeValue.trim()}"` : "") +
(node.nodeType === 1 ? ` [${node.nodeType}]` : ""));
for (let child of node.childNodes) {
walk(child, depth + 1);
}
}
walk(doc);
// 写法2:只遍历元素节点(推荐日常使用)
function walkElements(node) {
console.log(node.tagName);
for (let child of node.children) {
walkElements(child);
}
}
walkElements(root);
// 写法3:用 nextElementSibling 横向遍历所有 book
let book = root.firstElementChild;
while (book) {
console.log(book.getAttribute("id"), book.querySelector("title").textContent);
book = book.nextElementSibling;
}
4. 一句话总结
XML DOM 节点树 = 整个 XML 文档在内存里长成的一棵“家族树”
- 文档节点是“老祖宗”
- 元素是“人”
- 属性是“人身上的标签”
- 文本是“人说的话”
- 换行和空格也是“人”(空白文本节点),你得学会无视他们
记住这棵树 + 上面那张“家族关系表”,你就已经掌握了 XML DOM 70% 的精髓!
需要我画一个彩色的思维导图或给你一个可交互的 HTML 节点树演示页面,随时说一声~