html解析器在词法分析阶段将连续空白字符折叠为单个u+0020,仅在内例外;该过程早于dom构建与css渲染,决定空格是否存于dom中。

HTML 解析器对空格的处理不是“保留”或“删除”的二选一,而是按规范在词法分析阶段就完成折叠——连续空白(空格、制表符、换行符)一律转为单个 U+0020,再进入 DOM 构建。这意味着你看到的“多余空格消失”,根本不是 CSS 或渲染阶段的事,而是解析器早就干完了。
HTML 解析器如何折叠空白字符
浏览器在构建 DOM 树前,会先对 HTML 源码做词法分析:所有空白字符(\t、\n、\r、\f、 )被统一归一化为 U+0020;随后,连续出现的该字符被压缩为一个。这个过程不区分标签内外,但有上下文例外:
-
<pre class="brush:php;toolbar:false;"></pre>、<script></script>、<style></style>内部跳过折叠,原样保留 - 元素之间的空白(如
<div></div>\n<p></p>中的换行)会被折叠进相邻文本节点,但若两侧无文本节点,可能残留空Text节点 - 属性值中的空白也会被折叠(
class=" a b "→class="a b")
为什么 <button></button><input> 之间会多出 4px 间隙
这不是“空格没被删掉”,而是折叠后的那个空格被渲染成了真实像素——当元素是 inline 或 inline-block(如默认 <button></button>、<input>)时,源码中元素间的换行/空格会生成一个文本节点,其内容就是那个被折叠出来的单个空格;而该空格的宽度 = 当前 font-size × 通常为 0.25em 的比例,约等于 4px(16px 字号下)。
- 动态拼接字符串时(如
$('...').html('<button></button><input>'))没换行,自然没空格节点 - 静态写法中换行缩进虽可读性强,却悄悄引入了渲染间隙
-
element.normalize()不会消除这个间隙,它只合并/移除空文本节点,但不会动已有空格的nodeValue
用 white-space 控制空格显示的前提
white-space 是渲染层开关,它不改变解析结果,只改变已存在的文本节点如何展示。比如 white-space: pre 不会让解析器保留多个空格,而是让渲染引擎照着 DOM 中那个已被折叠成单空格的 Text 节点,把它当“不可折叠”来画——但它无法还原被解析器丢掉的那些空格。
-
normal:默认,折叠 + 换行 -
nowrap:折叠 + 不换行(空格仍存在,只是不让断行) -
pre:不折叠 + 不换行(实际是“不应用折叠后规则”,不是“撤销折叠”) - 真正想保留原始空格数?必须用
<pre class="brush:php;toolbar:false;"></pre>或实体、等显式插入
DOM 中空格节点的清理与误判
解析后 DOM 里常残留只含空格/换行的 Text 节点,它们看不见但占位置。调用 element.normalize() 可合并相邻文本节点并移除空节点,但要注意:
- 它不作用于未挂载的元素(
document.createElement('div').normalize()无效) - 它不修改文本内容本身(
"a b"不会变成"a b") - 它不能替代
white-space—— 清理完空节点,剩下那个带空格的文本节点依然受 CSS 控制 - 若需彻底去除所有空白影响,应结合
font-size: 0(父容器)+ 显式恢复子元素字号
最易被忽略的是:空格处理发生在解析器层面,早于任何 JS 或 CSS。你写的换行、缩进、多空格,在 DOM 构建完成那一刻,就已经被重写过了。想控制它,得从源头(写法)、中间层(实体/CSS)或后期(DOM 操作)三处下手,而不是指望某一个机制能“全局逆转”。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











