XMLSerializer不是还原手写HTML源码的工具,而是生成语法合法、结构等价的标准化HTML字符串;它自动补全标签、转义字符、统一自闭合形式、丢弃注释和非标准属性,仅保留真实DOM属性树中的内容。

XMLSerializer 不是“把 DOM 变成你写的 HTML 源码”的工具,它生成的是语法合法、结构等价的标准化 HTML 字符串——这点必须先确认,否则后续所有操作都会偏离预期。
为什么 serializeToString() 输出不像你手写的 HTML?
它不是 bug,是设计目标:输出可被 XML 或 HTML 解析器无歧义加载的字符串。这意味着:
-
<div>hello</div>会被补全为<div>hello</div>(看起来一样,但内部处理已标准化) -
<table><tr><td>x</td></tr></table>会自动插入<tbody> -
<img src="a.jpg">统一变成<img src="a.jpg" />(即使原始是 HTML5 风格) - 注释节点(
Comment)、<!DOCTYPE>、IE 条件注释全部丢弃 - 属性顺序重排,引号统一为双引号,特殊字符如
&、<全部转义
怎么正确调用 XMLSerializer.serializeToString()?
核心原则:只传 Node 实例(Element、Document、DocumentFragment),别传字符串或 innerHTML。
- 要序列化单个元素:
serializer.serializeToString(document.getElementById('app')) - 要序列化整个页面主体(不含
<html>外壳):serializer.serializeToString(document.body) - 要序列化脱离文档的片段(如
contenteditable内容):先用range.cloneContents()得到DocumentFragment,再传入 - 不能传
document.documentElement.outerHTML这类字符串——会报错TypeError: parameter 1 is not of type 'Node'
常见属性丢失?检查是否用了错误的设置方式
XMLSerializer 只保留 DOM 属性树中真实存在的属性,不读取 JS 属性映射:
-
el.className = 'a b'→ 不会生成class="a b"属性(el.hasAttribute('class')返回false) - 正确做法:
el.setAttribute('class', 'a b')→ 序列化后有class="a b" -
data-属性安全:只要命名符合data-xxx规范,就会保留 - 自定义属性如
myattr="1"、事件属性如onclick、onload全部被过滤(XML 规则不允许未声明的属性) - 内联样式(
style)会保留,但值是字符串形式,不反映运行时计算结果
想输出 HTML5 风格(比如不闭合 <input>)?得手动后处理
serializeToString() 默认按 XML 规则输出自闭合标签,而 HTML5 允许 <input>、<br> 等不闭合。若需匹配 HTML5 渲染行为:
立即学习“前端免费学习笔记(深入)”;
- 对结果执行正则替换:
str.replace(/]*)\/>/g, '') - 但注意避开 SVG 元素(如
<path/>、<circle/>),它们必须闭合 - 更稳妥的做法:只对已知 HTML5 空元素做替换,用白名单控制:
/(]*)\/>/gi - 别指望靠这个还原原始格式——缩进、换行、空格、引号风格依然全无
XMLSerializer 就不是合适工具;它适合 DOM 快照、XHTML 导出、模板 diff 或结构校验这类语义一致即可的场景。细节上最容易被忽略的是:它不处理 shadow DOM,也不保留动态绑定状态——这些都不是缺陷,而是边界。



















