参考文章:
深入了解CSS的line-height属性
Vertical-Align: 你需要知道的所有事【译】
Vertical-Align: All You Need To Know

1、什么是行间距或者行高(line-height)

line-height是指文本行基线间的垂直距离。

1.1、顶线,中线,基线,底线

从上到下分别是顶线,中线,基线,底线。vertical-align的四个属性top,middle,baseline,bottom就是与这四条线有关。

1.2、行高,行距,半行距

  • 行高是指上下文本行基线间的垂直距离。(上图中两条红线间的垂直距离)
  • 行距是指一行底线到下一行顶线的垂直距离。(第一条粉线和第二条绿线间的垂直距离)
  • 半行距就是行距/2。(图中可以看出,半行距=(行高-字体size)/2 )

1.3、内容区,行内框,行框

  • 内容区:顶线和底线包裹的区域(字体的size)
  • 行内框:在没有其他因素影响的时候(padding等),行内框等于内容区。而设定行高时行内框高度不变,半行距分别增加/减少到内容区的上下两边(深蓝色区域)行框(line box)。(字体size不变,修改行高就是修改行距)
  • 行框:行框高度等于本行内所有元素中行内框最大的值(以行高值最大的行内框为基准,其他行内框采用自己的对齐方式向基准对齐,最终计算行框的高度),当有多行内容时,每行都会有自己的行框。

1.4、line-height的设置

百分比方式设置

1
2
3
4
<body>
121212
<p>121212</p>
</body>

1
2
3
4
5
6
7
body{
font-size:16px;
line-height:120%;
}
p{
font-size:32px;
}

line-height的百分比(120%)和body的字体大小(16px),被用来计算(16*120=19.2),这个值会被层叠下去的元素所继承。

补充

1
2
3
4
5
p{
font-size:32px;
line-height:60px;
padding:10px
}

最终盒模型

盒模型中,内容(不是上文说的内容区,上文的内容区是顶线与底线间的区域)的高度等于line-height的值。为什么会有margin?浏览器默认P的上下margin是1em,设置了P的font-size是32px,所以1em=32px。上下margin就是32px。

长度方式(px)设置

1
2
3
4
<body>
121212
<p>121212</p>
</body>

1
2
3
4
5
6
7
body{
font-size:16px;
line-height:20px;
}
p{
font-size:32px;
}

值normal

1
2
3
4
<body>
121212
<p>121212</p>
</body>
1
2
3
4
5
6
7
body{
font-size:16px;
line-height:normal;
}
p{
font-size:32px;
}
body
body

body的line的line-height是22px,所以normal等于1.375

p的line-height:32px*1.375=44px(normal并不是精确的等于1.375)

纯数字
就是将normal改为一个想要的准确数字。

1.5、各种BOX

1
2
3
<body>
<p>这个<em>强调</em> 元素为行内元素</p>
</body>
1
2
3
4
5
6
7
8
body{
font-size:16px;
line-height:1.5;
}
p{
font-size:32px;
padding:10px;
}

containing box
p就是一个containing box,包含了其他boxs。

inline box
在段落内,有一系列的inline box,inline box不会让内容成块显示,而是排成一行。“强调”是一种inline box,“这个”,“元素为行内元素”为一种匿名inline box。

line box
多个inline box组成line box,多个line box组成containing box。

Content Area
Content Area是围绕着文字的一种看不见的box,高度取决与font-size

inline box与line-height
font-size:32px,line-height:48px,行间距=48px-32px=16px,半行间距=8px。
半行间距会用在Content Area的顶部和底部。

这里inline box的高度就是line-height。inline box包着Content Area

但是,当line-height小于font-size。line box的高度还是line-height,所以line-box的高度小于Content Area的高度,Content Area会溢出line-box。

inline box 与line box
line box的高度取决于他内部最高的inline box。

2、vertical-align

vertical-align是用来对齐内联级元素的。设置为以下display属性的元素,它们都被认为属于内联级元素。inlineinline-block or inline-table (本文中不涉及此种情况):

inline内联元素基本上是包裹文本的标签。

inline-block内联块元素则如它们的名字所示:拥有内联特性的块元素。他们可以有width和height(可能是由自己的内容定义),以及padding、border和margin。

内联级元素彼此紧挨着放在一行中。一旦有更多的元素被放置到当前行中,一个新的行将会在它下面创建。所有这些行有所谓的“行框”,行框中包含所有的内容。不同大小的内容意味着不同高度的行框。在下面的插图中,行框的顶部和底部都是用红线表示的。

在行框中,元素的vertical-align属性是负责垂直对齐的。那么,到底元素垂直对齐的参照物是什么?

参照物:父元素的基线和外边缘
看看元素的基线和行框的外观:
inline元素

三行并排的文本。行框的顶部和底部边缘用红线表示,字体的高度由绿线表示,基线由一条蓝线表示。在左边,有一个line-height设置为与字体font-size大小相同高度的文本,绿线和红线重叠在一条线上。在中间,line-height是字体的两倍大。在右边,line-height是字体大小的一半大。

内联元素(display:inline)的外边缘与其行高的顶部和底部边缘对齐,行高可以小于字体的高度。所以,行框就是上面的图中的红线。

内联元素的基线是字符放置的位置线(字母x底部所在的水平线),即图中的蓝线。粗略地说,基线是在字体1/2高度的下面的某个地方。

inline-block元素
inline-block因为已经有宽和高,可能存在多行,每行都有自己的基线和行框,所以会比较特殊。

上图中,最外层是div,里面分别是三个inline-block的span,黄色为border,绿色为padding,蓝色为content area(一个span,其中有一个字母“C”)。左边的inline-block的span的内容(span)是一个正常文档流元素。中间的inline-block的span还额外加了overflow: hidden。右边的inline-block的span包含一个流外的span(但内容区域有一个高度)(译者注:流内的元素必须是普通文档流(normal flow)中的元素,流外的元素必须是浮动或绝对定位的元素以及根元素。)。蓝线为每个inline-block的span的基线。内联块元素的外边缘是其margin框的顶部和底部边缘,即图中的红线。

内联块元素(上图三个inline-block的span)的基线取决它包含的内容是否在文档流中:

  • 在流内内容的情况下,内联块元素的基线是正常流中最后一个内容元素的基线(左边的例子)。对于这最后一个元素,它的基线是根据它自己的规则找到的。
1
2
3
4
5
6
<div class="demo1">
x<span>
x<span style="display:inline-block;height:30px;width:100px;background-color:blue">x</span>
x
</span>
</div>
1
2
3
4
5
6
.demo1 span{
display:inline-block;
background-color:silver;
height:90px;

}

灰色背景的元素内部有三个子元素,两个“x”,一个span。元素的基线就是最后一个正常流元素(“x”)的基线。
修改元素的长度,使其内容出现多行:

最外面的X怎么也跟着移动了?这涉及行框基线的移动,下文细说。

  • 在流内内容但内联块元素有overflow:hidden属性的情况下,基线是内联块元素margin框的底部边缘(例如在中间的图)。
    修改上面的例子样式:
    1
    2
    3
    4
    5
    6
    7
    8
    .demo1>span{
    display:inline-block;
    background-color:silver;
    height:90px;
    width:100px;
    margin:10px;
    overflow:hidden;
    }

通过最外面的x大致知道行框的基线位置,就是内联块元素的下外边距的地方,也是内联块元素元素的基线位置。
一开始此处有疑惑:内联块元素元素的基线跑到了下外边距处,那么元素里面的内容不应该以这条基线做定位吗?群里问了大佬,内联块元素已经设了宽高,可能有多行(即使只有一行),每行有各自的行框,然后又根据规则定位了,跟内联块元素的基线已经没有关系。

  • 在流外内容的情况下,基线是内联块元素margin框的底部边缘(例如在右边)。
1
2
3
4
5
<div class="demo1">
x<span>
<span style="display:inline-block;height:30px;width:100px;background-color:blue;">x</span>
</span>
</div>
1
2
3
4
5
6
.demo1>span{
display:inline-block;
background-color:silver;
height:90px;
width:100px;
}

加上浮动

1
2
3
4
5
<div class="demo1">
x<span>
<span style="display:inline-block;height:30px;width:100px;background-color:blue;float:left">x</span>
</span>
</div>

行框的基线是可变的
当使用vertical-align时,基线放置在哪里可能是最令人疑惑的部分。它需要满足vertical-align的值和行框的高度等所有条件。基线的位置犹如是方程中的一个自由参数。

行框的基线是看不见的,但你可以使它很容易看到。只要在文本行的开头添加一个字符,像我增加了一个“X”的字母。如果这个字符不以任何方式对齐,它将默认地坐在基线上。

围绕着行框的基线的部分(绿线),我们可以称其为文本框。文本框可以简单地被认为是行框内的内联元素,没有任何对齐。文本框的高度等于它的父元素的字体大小。因此,文本框只围住了行框内的无格式文本。由于这个文本框是绑在基线上的,当基线移动时它将移动。(注:此文本框在W3C规范中称为“strut(支柱)”)

vertical-align的值
1)将元素的基线,参照父元素的基线对齐

baseline:元素的基线与父元素的基线对齐。

sub:元素的基线偏移到父元素的基线之下。

sup:元素的基线偏移到父元素的基线之上。

:元素的基线相对于父元素的基线偏移了一个百分比(该百分比是对比元素自身的line-height计算得出)。

:元素的基线相对于父元素的基线偏移了一个绝对长度。

2)将元素的中心点,参照父元素的基线对齐

middle:将元素的顶部和底部之间的中心点,对齐父元素的基线之上x-height的1/2之处(x-height为字母x的字符高度)。

3)将元素的外边缘,参照父元素的文本框对齐

text-top:将元素的顶部边缘,对齐到父元素的文本框的顶部边缘。

text-bottom:将元素的底部边缘,对齐到父元素的文本框的底部边缘。

4)将元素的外边缘,参照父元素行框的外边缘对齐

top:元素的顶部边缘对齐到父元素的顶部边缘。

bottom:元素的底部边缘对齐到父元素的底部边缘。

基线的移动
如果一行中有一个高个的元素占据了整行的高度,那么vertical-align对它没有影响。它的顶部和底部没有空间让它移动。为了满足行框基线的对齐方式,行框的基线必须移动。矮个元素设置了vertical-align: baseline。在左边,高个元素设置了vertical-align: text-bottom。在右边,高个元素设置了vertical-align: text-top。你可以看到右边的基线跳起来了。

(左)将两个元素放在一行中并设置vertical-align ,它们会使得行框的基线移动到符合它俩的对齐规则之处,然后行框的高度也会随之调整。(中)添加第三个元素,不超越行框的边缘,既不影响行框的高度,也不影响基线的位置。(右)添加第三个元素,如果它超出了行框的边缘,行框的高度和基线调整。在这种情况下,我们的前两个元素也会跟着发生变化。

内联级元素底部的小间隙

列表项坐在基线上。下面的一点空间,是文本的基线以下预留的depth(在W3C规范中,一个字体的基线以上称为characteristic height,基线以下称为depth)。想要去掉这个depth空隙,有解决的办法吗?只要移动基线的位置就可以,例如通过设置列表项目vertical-align: middle

水平垂直居中

1
2
3
4
5
<div class="box">
<div class="content">
自适应垂直居中
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
html{
height:100%;
}
body{
height: 100%;
width: 100%;
}
.box{
display:inline-block;
text-align: center;
width:50%;
height:50%;
background-color:#e1e3cd;
overflow:hidden;
}
.box:after{
content:"";
display:inline-block;
height:100%;
vertical-align:middle;
}
.content{
vertical-align:middle;
background-color:silver;
display: inline-block;
width: 50%;
height:50%;
}

要将content水平垂直居中定位在box里,利用vertical-align是其中一种方法。原理是:vertical-align:middle(将元素的顶部和底部之间的中心点,对齐父元素的基线之上x-height的1/2之处(x-height为字母x的字符高度)。),content肯定是要垂直居中的,那只能修改行框的基线位置(注意:不是修改box的基线,box具有宽高,它里面的内容可能会有多行,每行有各自的行框,box的基线已经不会影响内容的布局,但是box的基线还是会受里面内容的影响(内联块元素的基线是正常流中最后一个内容元素的基线)),使其位于box的垂直中心位置。修改行框的基线,只要在box内加一个高度为100%的空元素,然后设置vertical-align:middle,添加的元素已经占满整个行框高度,而只要移动行框的基线,就可以满足定位规则,所以行框的基线就被移动到box垂直中心位置。content再按规则对齐到行框基线上就可以了。