XML DOM 节点详解(最全最清楚版)
XML DOM 把整个 XML 文档表示成一棵“节点树”,树上每一个东西都叫节点(Node)。
一共有 12 种节点类型,但日常开发中最常用的是前 5 种。
| nodeType 值 | 节点类型(常量) | 中文名称 | 说明 | nodeName | nodeValue |
|---|---|---|---|---|---|
| 1 | ELEMENT_NODE | 元素节点 | 就是标签 <book>、<title> 等 | 标签名(如 book) | null |
| 2 | ATTRIBUTE_NODE | 属性节点 | 标签的属性,如 category=”编程” | 属性名(如 category) | 属性值 |
| 3 | TEXT_NODE | 文本节点 | 标签里面的文字,最常见 | “#text” | 文本内容 |
| 4 | CDATA_SECTION_NODE | CDATA 节点 | <![CDATA[ 这里可以出现 < > ]]> | “#cdata-section” | CDATA 内容 |
| 5 | ENTITY_REFERENCE_NODE | 实体引用节点 | 极少用(如 &) | 实体名称 | null |
| 7 | PROCESSING_INSTRUCTION_NODE | 处理指令节点 | <?xml version="1.0"?>、<?xml-stylesheet?> | target(如 xml) | 内容 |
| 8 | COMMENT_NODE | 注释节点 | <!-- 这是一段注释 --> | “#comment” | 注释文字 |
| 9 | DOCUMENT_NODE | 文档节点 | 整个 XML 文档的根 | “#document” | null |
| 10 | DOCUMENT_TYPE_NODE | 文档类型节点 | <!DOCTYPE ...> | doctype 名称 | null |
| 11 | DOCUMENT_FRAGMENT_NODE | 文档片段节点 | 轻量级文档对象,用于临时存放节点 | “#document-fragment” | null |
| 12 | NOTATION_NODE | 记法节点 | DTD 中几乎不用 | notation 名称 | null |
99% 的实际开发只关心 1、2、3、8、9 这五种!
重要属性一览表(所有节点都拥有)
| 属性 | 说明 | 示例 |
|---|---|---|
| nodeType | 节点类型(1~12) | node.nodeType === 1 |
| nodeName | 节点名称 | 元素是标签名,文本是 “#text” |
| nodeValue | 节点的值(文本、属性、注释、CDATA 才有) | textNode.nodeValue = “内容” |
| textContent | 推荐:获取或设置当前元素及其后代所有文本 | element.textContent |
| parentNode | 父节点 | node.parentNode |
| childNodes | 所有子节点(NodeList,包括空格文本节点) | node.childNodes |
| children | 只包含元素子节点(HTMLCollection) | node.children |
| firstChild | 第一个子节点(可能为空白文本) | node.firstChild |
| lastChild | 最后一个子节点 | node.lastChild |
| nextSibling | 下一个兄弟节点 | node.nextSibling |
| previousSibling | 上一个兄弟节点 | node.previousSibling |
| attributes | 元素节点专有,返回 NamedNodeMap | element.attributes |
经典示例(JavaScript)
<?xml version="1.0" encoding="UTF-8"?>
<bookstore> <!-- 注释 -->
<book id="1" category="编程">
<title lang="zh">JavaScript 高级程序设计</title>
<author>扎卡斯</author>
<price>99.00</price>
</book>
</bookstore>
const doc = new DOMParser().parseFromString(xmlStr, "text/xml");
// 1. 文档节点
console.log(doc.nodeType); // 9
console.log(doc.nodeName); // "#document"
// 2. 元素节点 <bookstore>
const bookstore = doc.documentElement;
console.log(bookstore.nodeType); // 1
console.log(bookstore.nodeName); // "bookstore"
// 3. 元素节点 <book>
const book = bookstore.firstChild.nextSibling; // 注意跳过换行文本节点
console.log(book.nodeType); // 1
console.log(book.nodeName); // "book"
// 4. 属性节点 id="1"
const idAttr = book.attributes[0]; // 或 book.getAttributeNode("id")
console.log(idAttr.nodeType); // 2
console.log(idAttr.nodeName); // "id"
console.log(idAttr.nodeValue); // "1"
// 5. 文本节点 "JavaScript 高级程序设计"
const titleText = book.firstChild.nextSibling.firstChild;
console.log(titleText.nodeType); // 3
console.log(titleText.nodeName); // "#text"
console.log(titleText.nodeValue); // "JavaScript 高级程序设计"
// 6. 注释节点
const comment = doc.documentElement.firstChild;
console.log(comment.nodeType); // 8
console.log(comment.nodeValue); // " 注释 "
常见坑:空白文本节点
XML 中换行和缩进也会变成 #text 节点!
<book>
<title>三体</title>
</book>
实际上是:
book (元素)
├── #text (换行+空格)
├── title (元素)
│ └── #text "三体"
└── #text (换行+空格)
解决方法(推荐两种):
// 方法1:用 children(只取元素)
for (let child of parent.children) { ... }
// 方法2:过滤空白文本
for (let node of parent.childNodes) {
if (node.nodeType === 1) { // 只处理元素节点
// 处理
}
}
一句话总结每个节点
| 节点类型 | 你最常用它做什么 |
|---|---|
| 元素节点 | 取标签、遍历、增删改 |
| 属性节点 | 读写属性值 |
| 文本节点 | 读取或修改标签里面的文字(配合 textContent 更方便) |
| 文档节点 | 整个 XML 的入口 |
| 注释节点 | 偶尔读取或删除注释 |
掌握了这 12 种节点(重点前 5 种)+ 上面这些属性,你就真正掌握了 XML DOM 的底层原理,再复杂的操作也难不倒你!
需要每个节点类型的完整创建/修改/删除代码示例,随时说一声~