前言
line-height
和vertical-align
在 CSS 开发中是比较常用的,但用起来也并不简单,比如在使用中常常会碰到某些特殊字体进行混排或文字对齐图标的情况,也许会发现,无论使用哪种方法,总是感觉文字或图片向上或向下偏移了几像素,不得不专门对它们进行位移;或者还有在使用vertical-align:middile
不能实现垂直居中等等,为什么会出现这些情况呢?接下来我们详细说说。
几个相关概念
先来一起学习一下几个与line-height
和vertical-align
相关的概念。
基线
图中从上到下的线分别是顶线(top line
)、主线(mean line
)、中线(middle line
)、基线(baseline
)、底线(bottom line
),很像我们刚学英语字母时的四线三格,我们知道vertical-align
属性中有top
、middle
、baseline
、bottom
,就是和这几条线相关。
基线是西文字体设计与排版的概念,源自西文字母的主体底部(字母E的底部)对齐的位置。
对于中文字体,本身的设计上没有基线等说法,每个字都在一个方形盒子中。但是在计算机上显示时,也在一定程度上沿用了西文字体的概念,通常来说,中文字体的方形盒子中文字体底端在基线和底线之间,顶端在顶线下一点。
行高、行距、半行距和x-height
-
行高:也就是
line-height
,指文本行基线间的垂直距离。上图任意两条相同颜色的垂直距离也是行高。 -
行距:是指一行底线到下一行顶线的垂直距离,即第一行粉线和第二行绿线间的垂直距离。
-
半行距:行距的一半。
半行距 = (行高 - 字号) / 2
。在 CSS 中的margin-top
不是从文字的顶线算起,而是从顶线半行距的上方开始算起。同理,margin-bottom
是从底线半行距的下方开始算起。 -
x-height:x字高,是指字母的基本高度,精确地说,就是基线
baseline
和主线mean line
之间的距离。
font-size
前端开发中我们会使用font-size
来设置文字的大小,假设我们设置的字体是 12px
,就代表着顶线和底线的距离为12px
。
CSS中的四种“box”
在 CSS 中有四种内联盒子,分别是:包含框containing box
,行框line boxes
,行内框inline boxes
,内容区content area
。
- 包含框(
containing box
):每一个块(block)都算containing box
,它包含line boxes
,line boxes
的高度垂直堆叠形成了containing box
的高度。 - 行框(
line boxes
):块内的内容渲染的每一行,都可以看成是一个行框,也可以说,每一行都是一个行框。line boxes
的行数,由 block 的宽度及内容决定,当然是在不限制高度的情况下。一行内有多个行内框,一个一个的inline boxes
组成了line boxes
,行框是包含一行内行内框最高点和最低点。它的高度,由行内最大line-height
决定。 - 行内框(
inline boxes
):不会成块显示,而是并排显示在一行的boxes
,如span
、a
、em
等标签以及匿名inline boxes
(即包含没标签的裸露的文字)。 - 行内容区(
content area
):content area
是一种围绕文字看不见的box。content area
的大小与font-size
大小相关。一般选择文本后,有背景颜色的就是内容区了(下图橘色)。
<!-- html代码 -->
<div>
这里是一个div,里面包含了独立的文字,
<span>span标签</span>
<em>em标签</em>
等等,
以及其他的一些文字。
</div>
line-height
line-height
行高是指文本行基线之间的距离。行高line-height
实际上只影响行内元素和其他行内内容,而不会直接影响块级元素,也可以为一个块级元素设置line-height
,但这个值只是应用到块级元素的内联内容时才会有影响。在块级元素上声明line-height
会为该块级元素的内容设置一个最小行框高度。
line-height的值
值 | 描述 |
---|---|
normal | 默认值。行高取决于浏览器的解析,一般是1.2。子元素会单独计算 |
number | 设置数字,此数字会与当前的字体尺寸相乘来设置行间距,即number为当前font-size的倍数。大多数情况下,这是设置line-height 的推荐方法,不会在继承时产生不确定的结果 |
length | 设置固定的行间距,与font-size 无关,不会随着font-size 做相应比例的缩放,会被后代元素继承 |
% | 基于当前字体尺寸的百分比行间距 |
inherit | 从父元素继承 line-height 属性的值 |
line-height继承问题
line-height
是可以继承的。父元素不同的行高单位影响子元素的继承。例如:
- 父元素的行高为
15px
时,子元素直接继承此固定的行高 - 父元素的行高为
150%
或1.5em
时,会根据父元素的font-size
先计算出行高值然后再让子元素继承 - 父元素的行高为
1.5
时,根据子元素的font-size
动态计算出行高值让子元素继承
// 部分css代码
.px-line-height {
line-height: 15px;
}
.em-line-height {
line-height: 1.5em;
}
.percent-line-height {
line-height: 150%;
}
.nounits-line-height {
line-height: 1.5;
}
line-height与line boxes高度
为了引出line-height
与line boxes
,先说一下大家常见的现象。有一个空的<div></div>
,在没有设置height
值时,该div
的高度就是0。如果该div
里面打入了一个空格或是文字,则此div
就会有一个高度。那么大家思考过没有,为什么div
里面有文字后就会有高度呢?
可能有人会跟认为是:文字撑开的!文字占据空间,自然将div
撑开。我一开始也是这样理解的,但是事实上,深入理解inline
模型后,发现根本不是文字撑开了div
的高度,而是line-height
!要证明很简单(如下测试代码):
// css代码
.test1 {
font-size: 20px;
line-height: 0;
border: 1px solid #ccc;
background: #eee;
}
.test2 {
font-size: 0;
line-height: 20px;
border: 1px solid #ccc;
background: #eee;
}
<!-- html代码 -->
<div class="test1">测试1</div>
<div class="test2">测试2</div>
到底这个line-height
行高怎么就产生了高度呢?在inline box
模型中,有个line boxes
,这玩意是看不见的,这个玩意的工作就是包裹每行文字。所以一个没有设置height
属性的div
的高度就是由一个一个line boxes
的高度堆积而成的。
vertical-align
属性vertical-align
影响一个内联级元素垂直方向上的布局。具体适用于display
值为inline
,inline-block
或table-cell
的元素。
vertical-align的值
值 | 描述 |
---|---|
baseline | 默认。此元素(基线)放置在父元素的基线上。 |
middle | 使元素的中部(中线)与父元素的基线加上父元素x-height 的一半对齐(近似垂直居中) |
top | 把元素的顶端与行中最高元素的顶端对齐。 |
bottom | 把元素的顶端与行中最低的元素的顶端对齐。 |
text-top | 把元素的顶端与父元素字体的顶端对齐(字体很重要)。 |
text-bottom | 把元素的底端与父元素字体的底端对齐(字体很重要)。 |
sub | 垂直对齐文本的下标。 |
super | 垂直对齐文本的上标。 |
length | 使元素的基线对齐到父元素的基线之上的给定长度。可以是负数。0px 等同于baseline |
% | 使元素的基线对齐到父元素的基线之上的给定百分比,该百分比是line-height 属性的百分比,可以是负数。0% 等同于baseline |
inherit | 规定应该从父元素继承 vertical-align 属性的值。 |
需要注意的是:除了 top 与 bottom 是使元素相对于行内元素垂直对齐外,其他属性值都是相对于父元素。所以,在开发时,我们只需要关注当前元素和父级,两元素前后并没有直接影响。
top
<!-- html代码 -->
<div class="block">
<span class="child"></span>
<span class="top">子元素Aa</span>game
</div>
// 核心css代码
.block {
font-size: 50px;
}
.child {
display: inline-block;
width: 40px;
height: 80px;
margin-top: 20px;
}
.top {
vertical-align: top;
}
上图中,“子元素Aa”的vertical-align
设置成top
,那么它就与当前行框(line boxes
)里最高元素的顶端相对齐,顶端是包括margin
值的。
text-top
<!-- html代码 -->
<div class="block">
<span class="child"></span>
<span class="a">子元素Aa</span>
子元素Bb
<span class="c">子元素Cc</span>
</div>
// 核心css代码
.block {
font-size: 50px;
}
.child {
display: inline-block;
width: 40px;
height: 80px;
margin-top: 20px;
}
.a {
vertical-align: text-top;
font-size: 30px;
}
.c {
font-size: 80px;
}
上图中,父元素字体是50px
,那么与之相同“子元素Bb”的字体也是50px
, 所以可以看成是与“子元素Bb”的顶部对齐。
bottom
与text-bottom
和top
与text-top
区别同理。
几个常见现象与解决方法
接下来通过一些列子来看看日常开发中与line-height
和vertical-align
相关的一些现象。
图片下方的空隙
表现
先看看代码:
<div class="block">
<img width="200" src="abc.png">
</div>
// 核心出css代码
.block {
background: #eee;
}
看一下效果:
仔细看图片下发有一段距离的空隙,为什么会出现这个空隙呢?
在文章前边的内容中我们学习了元素默认情况下的对齐方式是通过基线baseline
对齐;而在浏览器中都有默认的字体的大小,这个空隙就是来源于这两个。如果给父元素设置个更大的字号,这个空隙也会变得更大。为了方便观察,我们添加个匿名inline boxes
(即没标签的裸露的文字),结果如下图,
如何去除这个空隙
- 让
vertical-align
失效。图片默认是内联级元素,而vertical-align
对块级元素无效。因此,我们只要让图片display
水平为block
就可以了; vertical-align
告别baseline
。设置为top
,bottom
,或者middle
等值;- 将
line-height
设置为足够小或者0
。下面的空隙高度,实际上是文字的行高计算值(line-height
)下边缘和字母x
基线的距离。因此,只要行高足够小,基线baseline
就会上移,自然,图片就会有容器底边贴合在一起了; - 将
font-size
设置为0
。font-size
的值间接控制了line-height
,font-size
设为0
, 本质上还是改变line-height
值; - 将
img
设置浮动或者绝对定位。
vertical-align:middle 没有绝对居中
表现
vertical-align:middle
这个属性值大家在平时开发中用得算是最多的了。先看一下下面的例子:
<div class="block">
<img width="200" src="abc.png">
x
</div>
// 核心出css代码
.block {
font-size: 100px;
line-height: 250px
}
img {
vertical-align: middle;
}
上图中,灰色背景为父元素;黄色线为父元素的垂直中线;绿色线为x
所在的基线,也就是父元素的基线;蓝色线为红色图片的垂直中线,且红色图片设置了middle
的对齐方式。按照定义就是:蓝色线与绿色线往上二分之一x-height
的高度所在的线对齐,也就是字母x
的中心点。如果绝对居中的话,黄蓝两条线应该完全重合。
为什么会产生这种现象呢?主要原因在于文字具有下沉特性(就是文字的垂直中心点在文字所在区域的中线往下沉一点,不同字体的文字下沉的幅度不同),从而导致蓝线无法绝对与黄线对齐。当文字大小足够小时,我们可以忽略。从而近似的实现居中效果。但是文字越大,影响就越明显。
如何解决
那对于这样的问题我们要怎么解决呢?以下提供两个思路(当然还有可能有其他更好的方案):
1、采用Flex布局中的align-items
属性,使子元素和父元素在垂直轴上居中对齐。代码也比较简单:
html代码同上。
// 核心出css代码
.block {
font-size: 100px;
line-height: 250px;
display: flex;
align-items: center;
}
2、 图中“x”字母(没标签的裸露的文字)可以受具有继承特性的CSS
属性影响,于是,我们可以通过其他设置来做调整,让字符的中线和字符内容中心线在一起,或者说在一个位置上就可以了。设置父元素font-size:0
, 因此此时content area
高度是0
,各种上面提到的线(基线、顶线、主线、底线等)都在高度为0
的这条线上,绝对中心线和中线重合。如下:
<div class="block">
<img width="200" src="abc.png">
<span class="a">子元素x</span>
</div>
// 核心出css代码
.block {
font-size: 0;
line-height: 250px
}
img {
vertical-align: middle;
}
.a {
font-size: 100px;
vertical-align: middle;
}
结论
本篇文章首先讲解了文字相关的一些基本概念和四种内联盒子的关系,以及line-height
和vertical-align
的基本属性和常见的各种表现,同时对一些实际应用中常见的现象做了简单的分析阐述,并为解决此类问题提供了思路。作者最后得出:
line-height
和vertical-align
都不是很靠谱css
中最难调的就是文字的样式