# 幽灵空白节点

# 一、问题现象

首先看一下什么是“幽灵空白节点”:

如图可见,我们的图片下面多出了一块 div 的区域,但是理论上 div 的高度由图片撑开,二者高度相等,那么这个多出来的一块原理是什么?

# 二、前提概念

在解决这个问题之前,需要了解几个概念:

# 2.1 vertical-align

首先,vertical-align (opens new window) 这个属性,用来指定行内元素(inline)、行内块级(inline-block)或表格单元格(table-cell)元素的垂直对齐方式。

也就是说,它对于块级元素(block)是无效的

其次,他的默认值是 baseline ,也就是元素的基线

# 2.2 内联盒子

内联盒子 概念张旭鑫博客里有详细解释 (opens new window),这里抽出重点概念:

img

图上的行级盒子 line boxes,是由好几个行内元素 inline boxes 组成的,正常情况下他们是下边缘对齐的,但是为什么是下边缘呢?

CSS2的可视化格式模型文档中有一么一段话:

The baseline of an ‘inline-block’ is the baseline of its last line box in the normal flow, unless it has either no in-flow line boxes or if its ‘overflow’ property has a computed value other than ‘visible’, in which case the baseline is the bottom margin edge.

中文翻译:

‘inline-block’的基线是正常流中最后一个line box的基线, 除非,这个line box里面既没有line boxes或者本身’overflow’属性的计算值而不是’visible’, 这种情况下基线是margin底边缘。

结合图片,首先每个内联元素是一定 都有 line-heightvertical-align 的!这个 ==非常重要!== 也就是说图内的这群行内元素,是以最后一个 line box 也就是“图片”这两字组成的盒子的基线(baseline)为对齐目标,也就是它的下边缘

关于字符的基线为啥是下边缘,参考文章 字母’x’在CSS世界中的角色和故事 (opens new window)

另外,有一些情况下内联盒子高度会为0

W3C规范中提到:

Line boxes are created as needed to hold inline-level content within an inline formatting context. Line boxes that contain no text, no preserved white space, no inline elements with non-zero margins, padding, or borders, and no other in-flow content (such as images, inline blocks or inline tables), and do not end with a preserved newline must be treated as zero-height line boxes for the purposes of determining the positions of any elements inside of them, and must be treated as not existing for any other purpose.

重点就是说:

如果一个line box里没有文字、保留的空格、非0的margin或padding或border的inline元素、或其他in-flow内容(比如图片、inline-block或inline-table元素),且不以保留的换行符结束的话,就会被视作高度为0的line box

# 三、如何解决幽灵空白节点

# 3.1 原理分析

根据上面的前提知识,我们来分析一下这个现象产生的原因:

在HTML5文档声明下,块级元素内部的行级内联元素会表现出一种行为:可以理解为在块级元素的内部(前后部分)会有看不见的没有宽度与实体的空白元素,你可以把他想象为一个空字符串节点这个假想又似乎存在的空白节点,称之为“幽灵空白节点”。

那么这个空隙是怎么来的呢?我们把一开始的代码稍作更改:

这里我们用一个 span 来模拟这个幽灵空白节点,上面我们说可以把他想象为一个空字符串节点,这里我加上了文字 rod 来撑开它并且加上背景色,可以很明显的看见,粉色的 span 有一部分在相对图片位置之下!造成这个现象的原因至此也就破案了:

  1. 内联的元素本身是有一定高度的(导致上下都多出来一些),这个高度的原因参考维基百科 (opens new window),这里只放一张图说明:

    x-height

  2. 内联盒子 里我们说了,默认情况下,会以所有内联元素的最后一个元素的基线(baseline)为对齐目标,也就是它的下边缘,如图👇

    罪魁祸首就是 vertical-alignline-height 这两兄弟导致下面撑开了那么一段。

# 3.2 解决方案

知道了原理,解决就简单了:

# ① 让 vertical-align 失效

图片是行内元素,而vertical-align对块状水平的元素无效,那么我们就把图片设置 block

img {
  width: 200px;
  display: block
}
1
2
3
4

# ② 使用其他 vertical-align 值

针对行内元素默认使用的是 vertical-align:baseline,我们只要把他改成任意其他属性就行,比方说bottom/middle/top都是可以的

img {
  vertical-align: middle;
}
1
2
3

# ③ 直接修改 line-height 值

通过修改块级元素的行高,让实际文字(也就是我们的幽灵节点)计算出的高度不足这个高度就可以。比方说,我们设置行高5像素

.parent {
  line-height: 5px;
}
1
2
3

# ④ line-height为相对单位,font-size间接控制

如果 line-height 是相对单位,例如 line-height:1.6 或者 line-height:160% 之类,也可以使用font-size间接控制,font-size 本质上还是改变 line-height

.parent {
  /* 设置为适合(足够小)的大小就可以,不一定是0 */
  font-size: 0;
}
1
2
3
4

# 四、其他

参考文章:CSS深入理解vertical-align和line-height的基友关系 (opens new window)

利用 vertical-align 特性结合 ::after 实现垂直居中对齐:链接

最后更新: 7/21/2022, 3:30:44 PM