CSS进阶(8)—— 内联元素的掌管者line-height和vertical-align(下)

736 阅读10分钟

上一章主要讲了line-height相关的知识,本章就来聊聊同样无处不在的vertical-align。vertical-align和line-height一样,都会影响元素在与水平流垂直方向上的表现,因此了解这两个属性,对于我们控制图文在垂直方向上的表现有很大的帮助。这里我用了"帮助",而不是必要,有人告诉我这本书不用一个字一个字看,现在看来确实如此,《CSS world》在某种程度上已经完全符合"钻牛角尖"的气质,但既然看了,就希望能把这个系列做完。

1.vertical-align的五类属性

抛开inherit这类全局属性不谈,个人把vertical-align分成五类,作者将第一类和第二类总结成线类。

(1)跟x字母打交道的线类:baseline/middle

(2)基于行框盒子边缘的线类:top/bottom

(3)跟父级基线相关的文本类:text-top/text-bottom

(4)常用于公式的上标下标类:super/sub

(5)具体数值类:2px/2em/20%等

通常情况下,内联元素默认都是沿着字母x的下边缘对齐的,也就是vertical-align的默认值是baseline。但对于图片等替换元素,往往使用元素本身的下边缘作为基线,这一点在过去的简陋布局中显得非常好用,二十年前的网页你甚至都不需要会写CSS就可以让读者正常浏览网站内容,但是如今这些"好用的"特点会给我们的CSS布局带来很多不便之处。下面我们通过实战来一一测试这些属性的表现以及利用这些表现实现一些布局方式。

2.跟x字母打交道的vertical-align:baseline/middle

前面说到了,vertical-align的默认值是baseline,也就是字母x的下边缘,为了更好的看清x的下边缘在哪里,我们需要借助最常用的替换元素——图片。图片的下边缘通常为元素本身,我们来看字母x和图片都基于baseline对齐会有什么表现吧。

<div>
    <span>字母x</span>
    <img src="../小和尚.jpg">
</div>

最终效果如下图所示:

可以看到,表现结果跟理论相同,x下边缘与图片下边缘完全持平,那么为什么中文"字母"的下边缘没有和x下边缘对齐呢?没为什么,大部分中文和部分字母的下边缘就是低于baseline的,别问,问就是不知道。同时,你会发现,图片的下边缘会有4px的空隙,导致最终容器的高度多了4px,很多人第一反应就是甩锅中文,因为中文看起来比字母x大一些,低一些,然而就算你把span标签删除了依旧会多出这4个像素。那么多出的4px是哪里来的呢?这里还要请出vertical-align的好朋友line-height一起来解释这个问题。

首先line-height的默认值是normal,这个值是根据font-size和font-family计算得到的,得到的值一般会大于字母x的高度,由于高度多出来的部分由line-height计算后在文字上下均匀分布,因此你可以理解为多出来的4px是baseline到文字下边缘的距离。因此,要解决这多出来的4px其实很简单,修改图片的vertical-align即可,如,让他基于字母x的中点对齐。当然这只是理论上,你可以根据实际需求解决这个问题。

修改后的结果如下图所示:

说完了baseline,再来聊聊middle。我不敢说很多人,我自己之前在理解vertical-align:middle的时候,认为CSS在父容器中间找了一条中线,然后让所有的图文基于这条中线均匀分布。事实上,这样理解的坏处是,你会认为vertical-align需要加在父容器上,事实上vertical-align只在两类元素内生效,就是内联元素和table-cell元素。换句话说,vertical-align属性只能作用在display计算值为inline、inline-block、inline-table或table-cell元素上。需要注意的是,浮动,绝对定位等让元素块化的操作也会让vertical-align失效。了解了vertical-align的生效范围后,我们来看看如何让内联元素在垂直方向上居中吧。依旧是刚才的例子,我们想让图片在固定高度的容器里用vertical-align:middle属性垂直居中。

<div>
    <span>字母x</span>
    <img src="../小和尚.jpg">
</div>
<style>
div{
    height: 400px;
    background: rgba(0,0,0,0.1);
    text-align: center;
}
img{
    vertical-align: middle;
}
</style>

代码的结果如下图所示:

结果似乎不尽如人意,图片并没有垂直居中,为什么会这样呢?其实答案已经很明显了,vertical-align:middle属性已经生效了,在由span和img生成行框盒子中,图片已经基于字母x的中点垂直居中了,那么,怎么让图片在父级容器中居中呢?这里又要请到好朋友line-height的帮助了,只要让line-height的值等于父容器的高度即可,当然,为了简化代码,我们可以去掉height的申明,让元素保持流的特性,自动撑开容器高度。

这里图片已经完美垂直居中了,但对于文字,可能会有一定的偏差,因为字母x的中点和许多文字的实际中点会有所偏差,这个居中属性在文字偏大的时候就显得明显偏下或偏上了。因此在实际使用的过程中,不要盲目迷信vertical-align的文字居中能力。

3.基于行框盒子上下边缘对齐的top/bottom属性

vertical-align:top;表示当前内联元素的顶部在垂直上边缘对齐。在table-cell中表示元素底padding边缘和表格行的顶部对齐。这里我们只讨论内联元素。下面我们通过一个实例来探究一下这句话的涵义。代码如下:

<div>
    <span style="font-size: 14px">14px</span>
    <span style="font-size: 16px">16px</span>
    <span style="font-size: 18px">18px</span>
    <span style="font-size: 20px">字母x</span>
    <img src="../小和尚.jpg">
</div>
<style>
div{
    height: 400px;
    background: rgba(0,0,0,0.1);
}
span{
    vertical-align: bottom;
}
img{
    vertical-align: bottom;
}
</style>

产生的结果如下,从下图可以很明显的看出来,内联元素的上下边缘对齐的边缘是行框盒子的边缘,不要混淆理解成了父级容器的边缘。

4.从了解到弃用:vertical-align的文本属性类

当你深入理解了这个属性之后,你就会发现这个属性很难有什么软用。(这句话写在开头是为了让你少钻点牛角尖)

作者在给vertical-align属性分类的时候,将baseline/middle/top/bottom分为一类,其实都是基于行框盒子进行划分的,只是个人由其表现差异性又细分成了两类。而文本属性类text-top/text-bottom在垂直方向布局的时候则和行框盒子没有多大关系了。

vertical-align:text-top 表示盒子的顶部和父级应有内容区域的顶部对齐。vertical-align:text-bottom 表示盒子的底部和父级应有内容区域的底部对齐。

内容区域指的是在默认状态下用鼠标选中文本时的背景蓝色区域,这里说的父级应有的内容区域指的是根据父级的font-size和font-family计算得到的内容区域。

有兴趣的可以参考[原文例子](https://demo.cssworld.cn/5/3-9.php)。这里就不深究这个看不到前途的属性了。

5.简单了解vertical-align的上标下标类属性

HTML里有两个标签,sup上标,和sub下标。

sup对应的默认vertical-align:super。

sub对应的默认vertical-align:sub

这两个属性无需多做说明,只需看一下他的具体效果即可。

<div>
    H<sub>2</sub>O<sup>[1]</sup>
</div>
<div>[1]:H<sub>2</sub>O是水的分子式</div>

由于markdown编辑器支持标签语言,因此我们可以直接预览最终效果如下(小提示:你可以通过浏览器直接检查下面的元素看到CSS样式)

H2O[1]
[1]:H2O是水的分子式

6.vertical-align被忽略的实用数值属性

作者在原文中嘲讽"很多即使工作很多年的前端开发人员,可能不知道vertical-align的属性值支持数值,更不知道支持负值",这着实让他感到意外,没错我就是那个可能过一万年都不会去知道这个值的程序员。在说明这个属性的好处之前,先来看一个利用vertical-align“垂直居中”元素的例子。


<div style="line-height: 150px;">
    <span style="vertical-align: middle;font-size: 50px">我想垂直居中,却有些偏下</span>
    <img src="../小和尚.jpg" style="vertical-align: middle;width: 50px;height: 50px">
</div>

从结果上来看,由于中文相对于x的middle表现的总体偏下,在文字偏大的时候这个问题就表现得尤为明显了,然而图片在这个时候表现得又比文字要好,如果是文字+图标(icon)的格式,往往会使得文字看起总是偏下,图标总有一些偏上,现在你知道锅应该甩给文字了。这个时候就要请我们的vertical-align数值属性来精确的调整文字的位置了。在不知道数值怎么设置的情况下,不如先来看看vertical-align:0,会发生什么,同时为了便于观察,把图片的垂直居中也改为基于基线对齐。

<div style="line-height:150px;position: relative;">
    <span style="vertical-align: 0;font-size: 50px">我的vertical-align是0,x</span>
    <img src="../小和尚.jpg" style="vertical-align: baseline;width: 50px;height: 50px">
</div>

这时候可以看到,vertical-align:0的表现和vertical-align:baseline的表现一模一样!因此,vertical-align:数值,是基于baseline对齐的,为了图片和文字最终能居中,我们当然先要保证图片先居中(因为图片替换属性的特性的总能保持完全居中),再去调整文字的数值。最终调整结果如下

<div style="line-height:150px;position: relative;">
    <span style="vertical-align: -10px;font-size: 50px">借助x辅助调整数值,x</span>
    <img src="../小和尚.jpg" style="vertical-align: middle;width: 50px;height: 50px">
</div>

除了数值,vertical-align还支持基于line-height的百分比计算数值,个人觉得没什么用就不说明了。

7.baseline在inline-block元素中的诡异表现

又到了CSS灵异事件的部分了,我觉得这些个灵异事件甚至可以单独抽出一个章节来讲。vertical-align属性的默认值baseline在文本之类的内联元素那里是指x的下边缘,对于替换元素则是替换元素的下边缘,看完这章应该对这两句话有很深的印象了,但是,如果是inline-block元素,规则就要复杂一些了。

一个inline-block元素,如果里面没有内联元素(包括匿名内联元素),且overflow不是visible,则该元素的基线是其margin盒子的底边缘,否则,其基线就是其元素里面最后一行内联元素的基线。

因为inline-block元素在实际开发中非常重要,搞清楚inline-block的诡异CSS是十分有必要的,理论看起太干,不如来个实际例子看看到底是怎么回事。

<div>
    <span style="margin-bottom: 10px"></span>
    <span>我有自己的内x</span>
</div>
<style>
div{
    background: rgba(0,0,0,0.1);
}
span{
    background: rgba(0,0,0,0.5);
    display: inline-block;
    width: 100px;
    height: 100px;
}
</style>

可以看到第一个inline-block元素由于没有内容导致其baseline位置发生改变,变成了margin盒子的下边缘,而另一个inline-block元素的基线是其最后一个行框盒子的基线,在本例中,就是第二行的x字母,由于inline元素的布局是基于baseline的,因此就出现了如图所示的"错位"。

除了空inline-block元素和非空inline-block元素会发生错位,行数不同的inline-block元素也会发生错位。如下图所示:

虽然这两种情况都会发生错位,但需要注意他们发生错位的机制完全不同!

想要解决这个错位问题其实很简单,只要你明白错位问题的产生的根本原因是inline-block元素和baseline有过节即可,所以inline-block元素的vertical-align可以用不基于基线的属性,由于数值属性也是基于基线的因此也会发生错位问题,所以需要具体问题具体分析,我只给出一种最简单的解决方案。如下所示

不忘初心,方得始终

喜欢博主的童鞋可以扫描二维码加博主好友~ 也可以扫中间二维码入驻博主的粉丝群(708637831)~当然你也可以扫描二维码打赏并直接包养帅气的博主一枚。