先记住这个答案
inline-block 元素在外部表现得像一个字,会和普通文字排在同一行。HTML 源码里标签之间的换行和缩进是空白字符,在默认的 white-space: normal 下会被合并成一个空格,于是每两个元素之间就夹了一个空格,看起来是一条几像素的缝。缝的宽度由父元素的字号和字体决定。去掉它最好的办法是父元素改成 display: flex,其次是删掉源码里的空白,或者在父元素上设 font-size: 0 再给子元素恢复字号。
- 缝是标签间换行缩进合并成的一个空格
- 缝宽跟着父元素的
font-size和字体变 - Flex 布局不会保留这类行内空白
font-size: 0能去缝,但子元素要重新设字号
缝是从哪里来的
排一行导航按钮,每个都设了 width: 25%,结果四个排不下,最后一个掉到了第二行。这就是那几条缝占的宽度加起来超过了 100%。
<!-- 写法一:每个标签一行,元素之间有换行和缩进 -->
<nav>
<a class="item">首页</a>
<a class="item">题库</a>
<a class="item">博客</a>
<a class="item">关于</a>
</nav>
<!-- 写法二:标签首尾相接,中间没有任何空白 -->
<nav><a class="item">首页</a><a class="item">题库</a><a class="item">博客</a><a class="item">关于</a></nav>
<style>
.item { display: inline-block; width: 25%; }
/* 写法一:三条缝加起来超过 100%,第四个换行
写法二:没有缝,四个正好排满一行 */
</style>两种写法的 CSS 一模一样,差别只在源码里有没有空白。浏览器把 </a> 和下一个 <a> 之间的换行加两个缩进空格当成一段文本,合并成一个空格后排进行框,它的宽度是这个字体里空格字符的宽度,16px 的常见字体下大约 4px,具体取决于字体。
- 为什么
display: block或浮动没有这条缝 - 块级元素各占一行,空格没有地方放,会被丢掉。浮动元素脱离了行内排版,空白文本留在行框里,但浮动元素本身不和它排在一起,所以也看不到缝。只有
inline和inline-block这种按文字规则排版的元素会被空格隔开。
几种去缝方法怎么选
去缝的思路只有两种:让空格不存在,或者让空格宽度变成 0。
- 首选:父元素改成 Flex
- 给
nav设display: flex,子元素之间只含空白的文本不会生成 flex 项,缝直接消失,子元素也不再需要inline-block。现在新写的横排布局基本都用 Flex 或 Grid,inline-block横排主要出现在老代码里。改成 Flex 时注意子元素默认会被flex-shrink压缩,width: 25%仍然有效,但内容太宽时可能被压窄。 - 父元素
font-size: 0 - 把父元素字号设成 0,空格宽度也就是 0,再给子元素写回
font-size: 16px之类的具体值。缺点是子元素不能再用em或百分比继承父元素字号,要么写px,要么写rem。以前一些旧版安卓浏览器对font-size: 0支持不好,现在基本不用考虑。 - 改源码:删空白或用注释接上
- 标签首尾相接,或者在
</a>和<a>之间插一个<!-- -->把空白吃掉,都能从根上去掉空格。缺点是可读性差,而且格式化工具可能会把换行加回来。用框架时情况又不一样:JSX 会丢掉包含换行的空白,Vue 3 模板默认的whitespace: 'condense'也会删掉元素之间带换行的空白,所以同样的标签写成多行,放进 React 或 Vue 组件里常常就没有缝了。只有写在同一行、中间隔着空格时,空格才会保留下来。 - 负
margin-left - 给子元素设
margin-left: -4px这类值抵消空格宽度。空格宽度随字体和字号变,换一个字体就不准了,只适合做临时修补。
容易答错的地方
- 以为是浏览器给
inline-block加了默认margin inline-block没有任何默认margin,开发者工具里看盒模型,margin是 0。缝是元素外面的一个文本节点,在 Elements 面板里能看到标签之间有一段空白文本。- 把底部的缝和左右的缝混为一谈
inline-block元素里放了图片或者和文字并排时,下面常常多出一条缝。那是vertical-align: baseline导致的:元素底边对齐到文字基线,基线下面还留着给 g、y 这类字母下沉的空间。这条缝要用vertical-align: top或middle解决,删空格没用。- 以为
word-spacing能去掉 word-spacing调的是词与词之间额外加的距离,对这个空格字符本身的宽度也有影响,理论上设成负值能抵消。但要设多少取决于字体,和负margin一样不可靠,一般不推荐。
面试官还会怎么问?
white-space: nowrap 能去掉缝吗?
不能。nowrap 只是禁止换行,空白仍然合并成一个空格。它反而常和 inline-block 一起出现,用来做横向滚动的卡片列表,这时缝还在,只是元素不会折到下一行。
Flex 容器为什么没有这个问题?
Flex 容器的子元素会被「块化」,直接的文本内容会被包进匿名 flex 项,但只包含空白的文本会被丢弃,不生成任何项。所以标签之间的换行缩进在 Flex 容器里没有任何渲染结果,Grid 容器也一样。
为什么空格宽度和字号有关?
空格就是一个字符,和汉字、字母一样由字体文件定义宽度,按当前字号缩放。父元素字号越大,缝越宽;换一种字体,缝的宽度也会变。这也是负 margin 修法不稳定的原因。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。