vertical-align:你想知道的都在这里(css)
作者:小辞 标签:Css

很抱歉,因为最近一个月一直很忙,所以一直都没有时间去更新小散文,直到前几天,终于抽时间完成了一个。

不过相关的概念一方面比较抽象,另一方面因为个人水平问题,翻译的不是很好,如果在阅读的过程中,遇到任何不明白的,都可以向我提问,我呢,也会竭尽所能解答大家的疑惑

然后开始正文:

 

我们来讨论一下CSS的属性vertical-align。它一般的用途,其实就是用来排列彼此相邻的文本和元素的。

但是有时候,他在工作的时候,也会有一些诡异的表现,让我们无所适从。比如说你可能碰见过这样的情况,当你改变了一个元素的vertical-align属性的值以后,这个元素的排列方式并没有发生变化,但是呢,紧挨着它的元素却变了。

因此,为了减少未来因此而造成的痛苦,我仔细研究了W3C的CSS规范,并且一次性的澄清了vertical-align的所有表现。

现在就让我们揭开它神秘的面纱吧

在本文中,你将会了解到如下内容:

  1. vertical-align作用于行框(line box)中的内联级元素。

  2. 内联级元素和行框都有baseline,top和bottom。

  3. vertical-align按baseline,top和bottom排列。

  4. 例子:如何居中文本旁边的图标。

  5. 例子:baseline是如何移动的。

  6. 例子:如何垂直居中一个元素,并且其底部没有空隙。

  7. 例子:如何消除两个对齐元素之间的空隙。

vertical-align作用于行框(line box)中的内联级元素。

vertical-align被用于排列内联级元素。其元素的display属性通常为:

inline,inline-block,inline-table(本文中,将不会讲解这个属性)

inline元素基本上就是用来包装文本的标记。

inline-block元素正如它的名字那样:内联块元素。他们有宽度和高度(通常由他自己的content所定义),与此同时也包括padding,border和margin。

内联级元素通常在一行中彼此相邻排列。每当有更多的元素加入到当前的行中,并且放不下的时候,一个新行就会被创建出来。所有的这些行都被称为“行框”(line box),它包围着所在行的所有的内容。不同大小尺寸的内容意味着不同高度的行框。在下面的插图中,行框的顶部和底部都用红线标识出来。

https://img2.mukewang.com/5c9313c7000158b107650192.jpg

行框清晰的展示出每一行起作用的区域。在行框里面,属性vertical-align负责对齐所在行每一个元素。因此,什么又是元素对齐呢?

 

关于baseline,top和bottom

垂直对齐的最重要的一个参考点就是包含元素的基线了。在一般情况下,元素所在的封闭的盒子的top和bottom也很重要。让我们看看每种涉及的元素类型的基线和外边缘所在的位置::

https://img4.mukewang.com/5c9313ed0001f03807710143.jpg

正如你所看到的,图中有三条彼此相邻的线。行高的上下边缘被标识为红色,字体的高度被标识为绿色,基线被标识为蓝色。在左图中,文本的行高(line height)设置为和font-size一样的大小,因此,绿线和红线重叠在了一起了。中间那个图呢,行高(line height)是font-size高度的两倍。右边呢,行高(line height)是font-size高度的一半。

内联元素的外边缘与其行高(line height)的顶部(top)和底部(bottom)对齐。如果行高比字体高度小的话,也没什么问题。因此,外边缘就是上图中的红线。

内联元素的基线是紧挨着字母的那一条线。在上图中用蓝线标识出来。粗略来说,基线是位于字体高度的中间偏下的某个部分。想了解详情可以看一下W3C的规范文档

(https://www.w3.org/TR/CSS2/visudet.html#leading)。

 

inline-block元素

https://img2.mukewang.com/5c9313fe0001173007770172.jpg

从左往右,你可以看到:左侧是一个有内容的inline-block元素,中间是一个具有内容的inline-block元素外加overflow:hidden,右侧是一个没有流内容的inline-block元素(但是内容区域具有高度)。margin的边界被标识为红色,border是黄色,padding是绿色而内容区域是蓝色。每一个inline-block元素的基线是一条蓝线。

inline-block元素的外边缘是margin盒子的上边缘和下边缘,在图中用红色标识。

inline-block元素的基线的位置依赖于元素是否有内容。

对于有内容的元素来说,在普通流中,inline-block元素的基线是最后一个内容的基线(例如左侧的例子)。

对于有内容的元素,但是其overflow属性值不为visible时,基线是margin盒子的底部边缘(例如中间的例子)。因此基线和inline-block元素的底部边缘是重合的。

对于非流特性内容来说,基线就是margin盒子的底部边缘(比如右侧的例子)。

 

行框(line height)

https://img3.mukewang.com/5c93140f0001ac1707690135.jpg

仔细看下上面的图。此时我在行框的文本盒子(text box)的顶部(top,绿色)和底部(bottom,绿色)还有基线(蓝色)上都画了线。此时,我也高亮了其文本元素,并给他们一个灰色背景。

行框的上边缘(top)与此线的最上面的元素的上边缘对齐,底边与线的最底部元素的下边缘对齐。 行框就是上图中红线所标识出来的方框的区域。

 

行框的基线是可变的:

“CSS2.1没有定义行框基线的位置” - W3C文档

这可能是vertical-align最有可能被混淆的部分了。这意味着,其位置受许多其他条件的影响,比如说vertical-align和最小化行框的高度。这些都是等式中的自由参数。

由于行框的基线是不可见的,所以它并不会立刻明显的显示出其位置。但是,你可以以一种非常简单的方式让其变得可见。在你感觉有疑惑的行的最前面增加一个字符,比如说在上图中增加的“x”字符。如果字符以任何方式都不会对齐的话。它默认一般都会坐落在基线上。

在基线周围,行框有我们一般会称之为文本盒子(text box)的内容。文本盒子可以简单的认为是在行框里面的一个没有任何排列的内联元素。它的高度等于父元素的font-size的高度。因此,文本盒子仅仅在行框中是一个未格式化过的文本。在上图中,这个文本框由绿线标识。由于文本框绑定到了基线上,因此当基线移动的时候,他也会跟着移动。(注意:文本盒子在W3C规范中被称为strut)(注意:其strut可以理解为上图中的字符x)。

这一部分比较难以理解,现在呢,我们已经把所有的事情都搞清楚了。然后呢我们来总结下一下相关的概念把。

这个区域被称为行框。垂直排列一般会发生在这个区域内。它有一个基线,一个文本盒子,和一个顶部和底部的边缘。

内联级元素是被排列的对象。他们有一个基线和顶部和底部的边缘。

 

vertical-align的对齐方式

https://img3.mukewang.com/5c93141d00016cc707630066.jpg

以下是vertical-align的属性值与其行框的基线的对应关系

baseline:元素的基线正好就位于位于行框的基线上。

sub:元素的基线在行框基线的下面。

super:元素的基线在行框基线的上面。

百分数:元素的基线会根据行框的基线,移动相对于行框高度的百分比。

数字:元素的基线会根据行框的基线,移动一个特定的长度。

特殊情况vertical-align: center有他自己的计算方式。

https://img4.mukewang.com/5c9314df0001e22807610045.jpg

middle:元素的顶部和底部的中点作为行框的基线,再加上x高度的一半(也就是字母“x”的交点位置)

相对于行框的文本盒子排列

https://img2.mukewang.com/5c9314f00001478407680073.jpg

由于文本框的位置由基线决定,因此也可以将下面这两种情况在相对于行框基线的对齐下进行排列。

text-top:元素的顶部边缘与行框的文本盒子(x)的顶部边缘对齐。

text-bottom:元素的底部边缘与行框的文本盒子(x)的底部边缘对齐。

相对于行框的外部边缘对齐

https://img4.mukewang.com/5c9314ff0001e49e07700074.jpg

top:元素的顶部边缘与行框的顶部边缘对齐。

bottom:元素的底部边缘与行框的底部边缘对齐。

 

正式的W3C标准的定义点这里。

https://www.w3.org/TR/CSS2/visudet.html#propdef-vertical-align

 

为什么vertical-align会按这样的规则去表现呢?

现在我们来看一下在特定情况下垂直对齐的一些表现。尤其是一些容易出问题的情况。

 

居中图标

 

有一个一直让我很烦的问题:我有一个图标,我想相对于紧挨着它的文本对齐。仅仅给icon一个vertical-align:middle似乎并不会让它以一种满意的方式居中。我们来看下这个例子:

<style type="text/css">
  .icon   { display: inline-block; }
  .middle { vertical-align: middle; }
</style>
<span class="icon middle"></span>Centered?
<!-- right mark-up -->
<span class="icon middle"></span><span class="middle">Centered!</span>

现在我们来画一些辅助线,来更好的理解。

https://img.mukewang.com/5c93150f0001341607620066.jpg

左边的文本看起来并没有对齐,因为其文字实际上是位于基线上。问题是,图标通过vertical-align:middle对齐,会把图标对齐到小写字母的中间(小写字母x的一半)。

对于右边的来说,我们把字体的整个区域都是按照中点(x的交点)对齐的。文本的基线很明显的移动到了行框基线的下面。其结果很好的居中了文本和图标。

 

行框基线的移动

当我们用vertical-align的时候,这个通常是一个陷阱:行框的基线的位置受这一行中所有元素的影响。我们来假设一下,一个元素以这样一种方式对齐 - 行框的基线一定会移动。由于绝大多数的垂直对齐都是相对于基线的,这就会导致在这一行中所有的其他元素都被影响。

例子:

如果在一行中,比较高的元素支撑起了整个的高度,vertical-align对他没有影响。它在顶部和底部都没有空间,也就是说,其没有足够的空间去移动。为了完成其相对于基线的对齐,其行框的基线一定会进行一些移动。短的盒子有一个vertical-align: baseline属性,对于左面来说,比较高的盒子是text-bottom对齐的。对于右面的盒子来说,其是text-top对齐的。你可以看到基线跳起来了。

https://img2.mukewang.com/5c93151f00014bfb07050173.jpg

<style type="text/css">
  .tall-box,
  .short-box   { display: inline-block; }

  .text-bottom { vertical-align: text-bottom; }
  .text-top    { vertical-align: text-top; }
</style>
​<!-- left mark-up -->
<span class="tall-box text-bottom"></span>
<span class="short-box"></span>

<!-- right mark-up -->
<span class="tall-box text-top"></span>
<span class="short-box"></span>

这是相同的表现,当高的元素和其他元素对齐的时候。

甚至设置vertical-align为bottom(左边)和top(右边)的时候,都会移动baseline。这很奇怪,因为baseline根本没有被涉及到

https://img3.mukewang.com/5c93154b0001834407020174.jpg

<style type="text/css">
  .tall-box,
  .short-box { display: inline-block;
  .bottom    { vertical-align: bottom; }
  .top       { vertical-align: top; }
</style>
<!-- left mark-up -->
<span class="tall-box bottom"></span>
<span class="short-box"></span>

<!-- right mark-up -->
<span class="tall-box top"></span>
<span class="short-box"></span>

吧两个大元素放进一行中,并且垂直的对齐他们,他们的基线会移动到他们都可以对齐的地方。然后行框的高度就是调整过的高度(左边)。另外如果再加一个元素的话,这个元素由于对齐方式将不会超出其行框的高度,行框的高度和基线位置都不会受影响(中间)。如果它确实超出了行框的边界,此时行框的高度和基线将会再次被调整。(右边)

 

https://img4.mukewang.com/5c93155e00012fd907130328.jpg

.tall-box    { display: inline-block; }
.middle      { vertical-align: middle; }
.text-top    { vertical-align: text-top; }
.text-bottom { vertical-align: text-bottom; }
.text-100up { vertical-align: 100%; }
<!-- left mark-up -->
<span class="tall-box text-bottom"></span>
<span class="tall-box text-top"></span>

<!-- middle mark-up -->
<span class="tall-box text-bottom"></span>
<span class="tall-box text-top"></span>
<span class="tall-box middle"></span>

<!-- right mark-up -->
<span class="tall-box text-bottom"></span>
<span class="tall-box text-top"></span>
<span class="tall-box text-100up"></span>

 

在内联级元素中可能会有一些空隙

我们来看一下下面的代码。这是很正常的,如果你吧列表中的li元素垂直居中的话。

https://img3.mukewang.com/5c93156d00018b3d07650103.jpg

<style type="text/css">
  .box { display: inline-block; }
</style>
<ul>
<li class="box"></li>
<li class="box"></li>
<li class="box"></li>
</ul>

你可以看到,列表元素坐落于基线上。在基线下面是一些用来容纳文本的下行部分。解决方案呢?用这种方式移动baseline。例如说通过给列表项增加一个vertical-align: middle来垂直居中他们。

https://img4.mukewang.com/5c93157d0001af1107650096.jpg

<style type="text/css">
  .box    { display: inline-block; }
  .middle { vertical-align: middle; }
​</style>
​<ul>
<li class="box middle"></li>
<li class="box middle"></li>
<li class="box middle"></li>
</ul>

对于具有文本内容的内联块,不会出现这种情况。因为内容已经移动到了baseline的上面了。

 

内联级元素间的空隙会破坏其布局

这主要是内联级元素本身的一个问题。但是由于他们需要vertical-align,因此了解他们是很重要的。

你可以在前面的list元素的例子中看到空隙。这个空隙来源于你的标签中的空白空格。所有的inline-block之间的空白都会塌陷进一个空间中。如果吧这两个元素放在一起,并且给他们width: 50%,那么他们就会妨碍我们的工作。例如两个50%的元素和一个空间是放不下的,因此他们会变成两行,破坏了了布局(左边)。为了移除空隙,我们需要移出空白,例如使用html注释(右边)

https://img3.mukewang.com/5c93158a0001ca3607620088.jpg

<style type="text/css">
  .half { display: inline-block;
    width: 50%; }
</style>
<!-- left mark-up -->
<div class="half">50% wide</div>
<div class="half">50% wide... and in next line</div>

<!-- right mark-up -->
<div class="half">50% wide</div><!--
--><div class="half">50% wide</div>

 

vertical-align不再神秘

现在来说,你知道了这些规则,感觉其也不是特别复杂了。如果vertical-align表现的和你预期不一致,那么可以问自己这些问题。

行框的基线和顶部和底部边缘在哪里?

内联级元素的基线和顶部和底部的边缘在哪里。

然后问题将会得到解决

 

觉得不错的话,可以关注微信公众号:干货技术,每周都有一篇前端相关原创干货小文章分享

 

翻译自:

https://christopheraue.net/design/vertical-align#vertical-align-acts-on-inline-level-elements-in-a-line-box