XML DOM – Text 对象(文本节点)
Text 是 XML DOM 中最常见的节点类型之一,代表元素或属性中的纯文本内容。
它继承自 CharacterData,又被 CDATASection 继承。
1. 基本信息
| 项目 | 值 / 说明 |
|---|---|
| nodeType | 3(Node.TEXT_NODE) |
| nodeName | 始终为 "#text" |
| nodeValue | 文本内容(可读写) |
| textContent | 与 nodeValue 完全等价 |
| data | 与 nodeValue 完全等价(CharacterData 接口) |
| length | 文本字符数量(UTF-16 代码单元长度) |
| parentNode | 通常是 Element 或 Attr(属性值也是 Text 节点) |
2. 重要方法(全部来自 CharacterData)
| 方法 | 功能 | 常用度 |
|---|---|---|
| appendData(text) | 在末尾追加文本 | ★★ |
| deleteData(offset, count) | 删除从 offset 开始的 count 个字符 | ★ |
| insertData(offset, text) | 在 offset 位置插入文本 | ★ |
| replaceData(offset, count, text) | 替换指定范围的文本 | ★★ |
| substringData(offset, count) | 提取子字符串(不修改原文本) | ★★★ |
| splitText(offset) | 关键!将当前 Text 节点从 offset 处劈成两个 Text 节点 | ★★★★★ |
| wholeText | 只读属性:返回当前节点及所有相邻连续 Text 节点的合并文本 | ★★★★★ |
3. 核心实战方法:splitText() —— DOM 操作的“神技”
<p>hello <b>world</b> !</p>
假设我们要只把 “world” 包上 ,但不能直接操作中间的文本,必须先拆分:
const p = doc.querySelector("p");
const textNode = p.firstChild; // "hello "
const rest = textNode.splitText(6); // 从第6个字符开始切分("hello " 之后)
// 现在文档结构变成:
// Text("hello ") + Text("world !")
const b = doc.createElement("b");
b.textContent = "world"; // 或 b.appendChild(rest.splitText(5))
p.insertBefore(b, rest); // 在剩余文本前插入 <b>
rest.splitText(5); // 把 " !" 再切出来(可选)
// 最终得到: <p>hello <b>world</b> !</p>
这就是所有富文本编辑器(包括浏览器内置的 document.execCommand、contenteditable)实现插入标签的底层原理。
4. wholeText —— 解决“多个相邻 Text 节点”问题
XML 序列化或某些操作会产生连续的 Text 节点:
elem.appendChild(doc.createTextNode("Hello"));
elem.appendChild(doc.createTextNode(" "));
elem.appendChild(doc.createTextNode("World"));
此时然通过 elem.textContent 或 elem.firstChild.wholeText 都能拿到完整文本:
console.log(elem.textContent); // "Hello World"
console.log(elem.firstChild.wholeText); // 同上,更高效
5. 创建与添加文本的正确姿势
// 推荐方式(自动转义 < > &)
element.textContent = "价格 < 100 元,且 > 50 元"; // 安全!
// 等价于:
element.appendChild(document.createTextNode("价格 < 100 元,且 > 50 元"));
// 错误示范(会变成真实标签):
element.innerHTML = "价格 < 100 元"; // 在 XML 中极度危险!
6. Text vs CDATASection(重点区别)
| 项目 | Text(普通文本节点) | CDATASection(CDATA 节点) |
|---|---|---|
| nodeType | 3 | 4 |
| nodeName | “#text” | “#cdata-section” |
| 是否转义 | 是(< 变成 <) | 否(原样输出) |
| 创建方式 | document.createTextNode() | document.createCDATASection() |
| 典型使用场景 | 普通内容 | 包含大量 <、>、&、脚本、XML 示例代码等 |
示例:
const cdata = doc.createCDATASection(`
<script>
if (a < b && b > c) {
alert("Hello <world>");
}
</script>
`);
doc.documentElement.appendChild(cdata);
// 序列化后原样输出,不会被转义
7. 常见坑 & 注意事项
| 坑点 | 正确做法 |
|---|---|
| 直接用 innerHTML 设置 XML 内容 | 绝对禁止!会破坏 XML 结构 |
| 以为 childNodes 只包含元素 | Text 节点、注释、处理指令都在里面 |
| 修改 text.nodeValue 后没反应 | 某些旧 IE 需要先 normalize() 再改 |
| 多个连续 Text 节点导致样式/事件异常 | 用 node.normalize() 合并相邻文本节点 |
// 合并相邻文本节点(推荐在操作完成后调用)
element.normalize();
// 反之:禁止合并(某些编辑器需要保持拆分状态)
element.firstChild.splitText(0); // 制造两个空文本节点阻止合并
8. 总结一览表
| 特性 | Text 节点 | 推荐操作方式 |
|---|---|---|
| 内容是否转义 | 是 | 日常文本用 Text |
| 包含大量 < > & | 会被转义 | 改用 CDATASection |
| 获取完整文本 | .wholeText 或 .textContent | 优先用 wholeText |
| 拆分文本插入标签 | splitText(offset) | 富文本编辑核心技术 |
| 合并相邻文本 | parent.normalize() | 操作完成后建议调用 |
一句话结论:
XML DOM 中 90% 的实际内容都存在 Text 节点里,掌握
createTextNode、splitText、wholeText、normalize四个关键点,你就掌握了 XML/HTML 文本操作的全部底层原理。