我们在之前的章节当中介绍过了 CSS 当中的 float,display,position 和 line-height 属性,今天我们接着上回继续来看与 line-height 联系十分紧密的 vertical-align,可以参考下面这个例子来看看 vertical-align 的基本用法(转自 MDN)
还是老规矩,我们从一个示例开始进行介绍,如下
1 | <div></div> |
我们页面当中有有两个 div 元素,第一个为空,第二个中间我们添加了一些文本,应用的样式如下
1 | div{ |
那么请问,此时页面当中的显示是什么样子的?运行以后你可能会很惊讶,因为结果是下面这样的

至于解决办法,很简单,只需要给第二个 div 加上 vertical-align: top 即可,那么为什么会这样呢?这也是我们今天将要探讨的问题
vertical-align
vertical-align 用来指定行内元素(inline)或表格单元格(table-cell)元素的垂直对齐方式,也就是说,对于块级元素,vertical-align 是不起作用的,vertical-align 的属性值可以归为以下四类
- 线类,如
baseline、top、middle、bottom - 文本类,如
text-top、text-bottom - 上标下标类,如
sub、super - 数值百分比类,如
10px、1em、5%
线类
线类当中用的较多的是 middle,但是我们还是一个一个来进行了解,先从 baseline 开始看起
baseline
baseline 为 vertical-align 的默认值,其意思是指基线对齐,所谓基线,指的是字母 x 的下边缘(关于基线的概念可以参考之前的 line-height 章节)我们来看个例子,代码如下
1 | .box { |
1 | <div class="box"> |
由于 baseline 是默认值,所以可以不用写,父元素的 line-height 为 100px,这其实是给默认空白节点设置的,我们在之前介绍过,每一个行框盒子都有一个看不见的节点,该节点继承了 line-height,因此子元素对齐于该节点的基线(可以想象成这个看不见的节点有一个字母 x,而子元素就是跟这个字母 x 的下边缘对齐),效果如下

关于 baseline 有一个需要注意的地方就是 inline-block 元素,如果一个 inline-block 元素,里面没有内联元素,或者 overflow 不是 visible,则该元素的基线是其 margin 底边缘,否则其基线就是元素里面最后一行内联元素的基线,其实就是我们开头时候看到的问题,例子如下
1 | .text { |
1 | <div class="container"> |
效果如下

top
对于内联元素,指的是元素的顶部和当前行框盒子的顶部对齐,对于 table-cell 元素,指的是元素的顶 padding 边缘和表格行的顶部对齐,例子如下
1 | .box { |
1 | <div class="box"> |
效果如下

其中 bottom,跟 top 类似,将顶部换成底部即可,所以我们就不展开了,下面来看看 middle
middle
这个属性值用得比较多,对于内联元素是将元素盒子的垂直中点与父盒子的 baseline 加上父盒子的 x-height 的一半位置对齐,简单点说就是字母 x 的中心位置对齐,对于 table-cell 元素,指的是单元格填充盒子相对于外面的表格行居中对齐
基本上所有字体中,字母 x 的位置都是偏下一点的,font-size 越大偏移越明显,因此字母 x 中心的位置不是行框盒子的中心,也就是说 vertical-align 只能实现近似垂直居中对齐
文本类
文本类分为以下两种
text-top,指的是盒子的顶部和父级内容区域的顶部对齐text-bottom,指的是盒子的底部和父级内容区域的底部对齐
看下面这个例子
1 | .box { |
1 | <div class="box"> |
效果如下

所谓内容区域,可以看成是鼠标选中文字后高亮的背景色区域,上面的例子中,由于父元素设置的是 20px,所以图片的 vertical-align 设置 text-top 的时候,就可以看成是跟子元素为 20px 元素的内容区域顶部对齐
上标下标类
上标和下标对应着两个标签 super 和 sub,super 在上面,sub 在下面,这两个属性值在数学公式和化学表达式中用得比较多,平时我们开发几乎用不到,所以这里就不在展开了
数值百分比类
vertical-align 是支持数值的,并且兼容性也非常好,但大部分开发人员却不知道 vertical-align支持数值,对于数值,正值表示由基线往上偏移,负值表示由基线往下偏移,而百分比则是基于 line-height来计算的,百分比用得比较少,因为 line-height 一般都是开发人员给出的,这时候数值就可以精确定位元素,不需要再使用百分比再去计算一遍,使用数值的代码如下
1 | .box { |
1 | <div class="box"> |
效果如下

vertical-align 起作用的前提
vertical-align 起作用是有前提条件的,这个前提条件就是,只能应用于内联元素以及 display 值为 table-cell 的元素,在 CSS 中,有些 CSS 属性是会改变元素的 display 值的,例如 float 和 position: absolute,一旦设置了这两个属性之一,元素的 display 值就是变为 block,因此 vertical-align 也就失去了作用,下面这段代码这样写就是错的
1 | span { |
另外,更多人遇到的是以下这种无效的情况
1 | .box { |
1 | <div class="box"> |
其实,不是 vertical-align 无效,而是前面所提到的行框盒子当中的空白节点引起的原因,由于父元素没有设置 line-height,所以当前空白节点的 line-height 就非常小,比图片的高度小很多,vertical-align: middle 没法发挥作用,这时给父元素一个比较高的 line-height,就会看到 vertical-align 起作用了
1 | .box { |
vertical-align 与 line-height 的关系
前面我们提到,vertical-align 的百分比值是根据 line-height 来计算的,但实质上只要是内联元素,这两个元素都会同时在起作用,如下例子
1 | .box { |
1 | <div class="box"> |
效果如下

从代码上看,好像父元素的高度会是 32px,但实质上父元素的高度会比 32px 还要高,原因是空白节点继承了 line-height: 32px,span 也继承了 line-height: 32px,但两者的 font-size 不一样,这就导致了空白节点的 font-size 比较小,而 span 的 font-size 比较大,也就是说它们的基线不在同一位置上,空白节点偏上一点,而 span 默认又是基线对齐,为此,span 总体会往上移以便跟空白节点的基线对齐,父元素元素就是这样被撑高了,而解决方案可以有以下几种
span元素不使用基线对齐,可以改为top对齐span元素块状化line-height设置为0font-size设置为0
关于边界和 baseline
对于垂直对齐这个知识点来说最重要的就是涉及元素的 baseline,有时候元素的盒模型的上下边界也会变的很重要,在上面我们曾提到过 vertical-align 起作用的前提,也就是 display 值为 inline 和 inline-block 的元素(inline-table 的元素不在本文的讨论范围内),下面我们就来看看它们之间的区别
inline

如图所示有三行文字,行高的上下边界是红线,文字的上下边界是绿色的线,蓝色的线就是 baseline 了,左边文字的高度与行高是一致的,因此绿线和红线重合了,中间的行高是文字大小的两倍,而在右边,行高是文字大小的二分之一
行内元素的外边缘在行高的上边缘和下边缘这个范围内对齐,如果行高小于文字的高度也无所谓,关于 baseline 的定义可以参考 leading
inline-block

从左到右的三张图都是 inline-block 元素,不同的是,左面包含着没有脱离正常流的内容 C,中间的除了没有脱离正常流的内容以外还加了 overflow: hidden,右面的没有内容但是内容区还有高度,红线代表了 margin-box 的边界,黄色代表的是 border,绿色的是 padding,蓝色的是 content,蓝色的线代表的还是 baseline
我们可以发现 inline-block 元素的外边缘就是 margin-box 的边缘,inline-block 元素的 baseline 的位置要看该元素有没有处于正常流之内的内容,所以这里可以分为三种情况
- 在有处于正常流内容的情况下,
inline-block元素的baseline就是最后一个作为内容存在的元素的baseline,这个元素的baseline的确定就要根据它自身来确定了 - 在
overflow属性不为visible的情况下,baseline就是margin-box的下边界了 - 第三种情况下
baseline还是margin-box的下边界
line-box

这一次我们将文字部分高亮显示,可以发现 line-box 的上边界与最高元素的上边界对齐,下边界与最低元素的下边界对齐
line-box 的 baseline 是不可见的,但是可以很轻松的将它可视化出来,在行的开头添加一个字母,比如 x,这个字母的下边界默认就是 baseline 的位置,围绕着 baseline 在 line-box 中形成了文字盒,文字盒可以被认为是没有和任何元素对齐的 line-box 中的 inline 元素,因此文字盒仅仅包含非格式化的 line-box 的文本,文字盒的边界由绿线来表示,因为文字盒是紧挨着 baseline 的,所以 baseline 的位置发生变化的话,文字盒的位置也会跟着改变(这里所提到的文字盒其实就是我们之前介绍到的空白节点)
总结起来的话有以下两点
- 有一个区域叫做
line-box,垂直方向上的对齐都是发生在这个区域里面,它有baseline,有文字盒,有上下边界 inline元素也有baseline和上下边界,inline元素是需要对齐的对象
line-box 的 baseline 的移动问题
这是一个 vertical-align 的坑,line-box 中的所有元素都会影响到 baseline 的位置,假设,一个元素按某种方式垂直对齐了,但是这种对齐方式会引起 baseline 的移动,又因为大部分的垂直对齐方式(除了 top 和 bottom)和 baseline 有关,因此这个元素的垂直方向对齐的行为会引起该 line-box 内其他元素位置的调整,我们来看下面这个例子
一个很高的元素,其高度占满了整个 line-box,那么 vertical-align 对其实没有影响的,在它的 top 和 bottom 之外没有空间让其移动,但是为了满足它的 vertical-align 的值,line-box 的 baseline 会发生移动,左面的高元素的取值为 text-bottom,矮元素的取值为 baseline,如果右面的高元素的取值为 text-top,你会看到 baseline 跳上去了

1 | <!-- left mark-up --> |
1 | .tall-box, |
如果把高元素的 vertical-align 设置为其他值,也能看到类似的行为,甚至将 vertical-align 设置为 bottom 或者是 top 也会让 baseline 发生移动,这很奇怪,因为这时候应该就没 baseline 什么事儿了,如下图所示

1 | <!-- left mark-up --> |
1 | .tall-box, |
将两个更大的元素放在一个 line 里面,并且设置 vertical-align 的值让 line-box 的 baseline 移动,在满足 vertical-align 数值对齐的条件下,line-box 的高度会自我调整,如左面的图,再增加第三个元素,第三个元素如果因为其 vertical-align 的设置不会超过 line-box 的边缘的话,它是不会影响到 line-box 的高度和 baseline 的位置的,如果它会超过 line-box 的边缘,那么 line-box 的高度和 baseline 的位置也会进行调整,在第二种情况下,另外两个元素的位置发生了下移

1 | <!-- left mark-up --> |
1 | .tall-box { display: inline-block } |
使用场景
其实我们可以发现,在大部分的情况之下,使用 line-height 就可以解决 vertical-align 所遇到的问题,这么说的话是不是 vertical-align 就派不上用场了呢?
在这样的场景下可以尝试使用 vertical-align,即当一个文本后面跟着个 inline-block 的元素时,后者是对其文本的基线的,这就导致文本看上去被挤下来了一样,使用 vertical-align: top/text-top 就可以解决这个问题,这是 line-height 所解决不了的