我们在之前的章节当中介绍过了 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
设置为0
font-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
所解决不了的