stephen's blog

[object Object] object(s)
 

[译]关于vertical-align你应该知道的事

原文链接:Vertical-Align: All You Need To Know
原文作者: Christopher Aue

关于Vertical-Align你应该知道的事

我经常会碰到将元素在垂直方向上对齐的需求。

CSS提供了一些可能的方法:有时我会用float来解决这个问题,有时会使用position:absolute,有时甚至不惜手动调整margins或者paddings(这是一件十分dirty的做法)。

实话说我非常不喜欢这些解决方式。浮动只在其顶部对齐并且需要手动清除浮动。绝对定位会使元素脱离文档流,以至于它们不再影响它们周边的元素。对于使用固定的marginspaddings来说即使只是一些微小的变化,那也将成为一场灾难。

但是这里有另一个杀伤力武器 : vertical-align。我认为它值得这个称赞。好吧,从技术角度上讲,使用vertical-align来布局其实是一种hack,原因是它存在并不是为了布局。而是为了使文本元素和与相邻的元素在垂直方向上对齐。尽管如此,在不同的上下文你依然可以非常灵活和细粒化的使用vertical-align来对齐元素。元素的尺寸可以不需要知道,因为元素没有脱离文档流,所以其它元素可以对那些改变的尺寸作出反应。这将使他在布局时成为一个有价值的选择。

Vertical-Align的奇特之处

尽管如此,vertical-align有时候可能是面目狰狞的。使用它会令人有点泄气:在它的内部工作机制中似乎有一些神秘的规则。比如有一种很可能发生的情况便是,当你改变了某个元素的vertical-align属性,实际上并没有改变它的对齐方式,但别的元素却发生了变化!

不幸的是,当今大部分有关这方面的资料深入程度都不够。尤其是当我们想用vertical-align来布局的时候。这些资料更多的是将注意力集中在一个误解的概念上-尝试着垂直对齐一个元素内的任何内容。他们仅仅给出基本的介绍以及解释在一些十分简单的情况下元素是如何对齐的。他们并不解释复杂的部分。

所以,我给自己设定了一个目标– 彻彻底底的弄清楚vertical-align 的行为 。我参考了W3C上的CSS规范以及试验了一些例子最终结束了这项任务。这篇文章便是我研究的成果。

那么,让我们来弄清楚这个游戏规则吧!

使用Vertical-align的要求

vertical-align被用于对齐inline元素。这些元素,也就是display属性为inline和inline-block,而inline-table的元素不在本文讨论的范围内。
Inline元素基本上指的就是文本。

Inline-block元素顾名思义,同时具备inline以及block元素的特性。这样的元素具有宽度和高度(可能是由内容高度决定),同样还具有padding,margin,boreder。
inline元素一个一个挨着摆放在行内。一旦元素太多而超出该行,则一个新行会被创建出来,这些行便是所谓的line-box。它会将行内所有的内容包裹起来。不同大小的内容,line-box的行内高度也会不同。下图画的红线代表了line-box的上下边界。

在这些line-box里vertical-align属性负责对齐那些独立的元素。那么,这些元素是和谁对齐呢?

关于Baselines和边界

关于垂直对齐最重要的一点便是相关元素的baseline。有些情况元素盒模型的上下边界也十分重要。让我们来看看每个相关元素中baseline和边界所在的位置。

上图你可以看到有并排的三行文字。行高的上下边界用红线标出,文字高度用的是绿线,baseline便是蓝线了。左图行高和文字高度一致,所以绿线和红线重合了。中间图行高是文字大小的两倍。右边行高是文字大小的二分之一。
行内元素的外界在行高的上下边缘这个范围内对齐。如果行高小于文字高度也无所谓。
什么是baseline呢?简单粗暴的说,baseline就是文字高度中线下方的一条线。具体可以看W3C上的介绍标准链接

inline-block元素

从左到右你可以看到三个inline-block元素:左边是没有脱离文档流的内容c,中间是没有脱离文档流的内容c并且overflow:hidden,右边是没有内容但是内容有高度。margin-box的边界是用红线标出,黄色是border,绿色是padding,蓝色是content。蓝色的线代表的是元素的baseline。
inline-block元素的边界就是margin-box的上下边界。

inline-block元素的baseline取决于该元素是否具有处于正常流的内容,具体分以下三种情况:

Line box

关于line-box的图上面已经说过了。这次我将linebox的内容的上下边界(绿线)以及baseline(蓝线)画了出来。同样将内容元素用灰色背景高亮了出来。
line-box的上边界和最高元素的上边界对齐,下边界和最低元素的下边界对齐。

line-box的baseline是不确定的

CSS2.1 does not define the position of the line box’s baseline ——the W3C Specs*

这也是当我们使用vertical-align时最令人疑惑的地方。这意味着baseline的位置受其他条件比如vertical-align的值以及让line-box高度最小的影响。这是一个很灵活的参数。

因为line-box的baseline是不可见得,所以可能不能一眼就看出来它在那。不过,你可以很轻易的将它可视化出来。只需要在行的开头添加一个字母。比如添加一个’x’。如果这个字母没有被其他方法来对齐,那么它的下边界默认就是baseline的位置。

在line-box中围绕着baseline会形成text-box。text-box可以简单的被认为是没有和line-box中任何元素对齐的inline元素。它的高度与父元素font-size的值相等。因此,text-box仅仅包含非格式化的line-box文本。text-box的边界由绿线来表示。因为text-box和baseline是绑定的,当baseline位置发生变化时它的位置也会发生变化。(这里所说的text-box在W3C标准中被叫做strut。)

hu~这部分比较难理解。让我们总结一下最重要的两点:

Vertical-Align的值

关于Vertical-align的值具体可以看MDN

Vertical-Align是如何发挥它的作用的呢

我们现在可以更加深入的了解在某些情况下元素是如何垂直对齐的。

对齐一个Icon

这里有一个问题:我想将一个icon与相邻的文本对齐。仅仅将icon的vertical-align属性的值设置为middle并不是一个令人满意的方法。看一下例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- left mark-up -->
<span class="icon middle"></span>
Centered?
<!-- right mark-up -->
<span class="icon middle"></span>
<span class="middle">Centered!</span>
<style type="text/css">
.icon { display: inline-block;
/* size, color, etc. */ }
.middle { vertical-align: middle; }
</style>

下面给出上图的辅助线版本:

这揭示了我们的问题。因为左图的文本根本没有发生对齐行为,它还是和line-box的baseline对齐。而vertical-align对齐的点是baseline加上半个x的距离(half of the x-height)。因此文字的最高点超过了icon的高度。
右图所示,文字与icon对齐与一个中点。文字的baseline稍微低于line-box的baseline。结果文字很好的和icon对齐了。

关于Line-box的baseline的移动问题

这是一个使用vertical-align常有的坑:line-box里的所有元素都会影响到baseline的位置。让我们假设一下,如果一个元素通过某种方法对齐了,但是line-box的baseline不得不因此移动。因为大部分对齐方式(除了top和bottom)都和baseline有关,因此这个元素的对齐会导致行内其他元素的调整。
下面是一些例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- 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>
<style type="text/css">
.tall-box,
.short-box { display: inline-block;
/* size, color, etc. */ }
.text-bottom { vertical-align: text-bottom; }
.text-top { vertical-align: text-top; }
</style>

当把高元素的vertical-align属性设置成其他值的时候,表现的行为是一样的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- 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>
<style type="text/css">
.tall-box,
.short-box { display: inline-block;
/* size, color, etc. */ }
.bottom { vertical-align: bottom; }
.top { vertical-align: top; }
</style>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!-- left mark-up -->
<span class="tall-box text-bottom"></span>
<span class="tall-box text-top"></span>
<!-- mark-up in the middle -->
<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>
<style type="text/css">
.tall-box { display: inline-block;
/* size, color, etc. */ }
.middle { vertical-align: middle; }
.text-top { vertical-align: text-top; }
.text-bottom { vertical-align: text-bottom; }
.text-100up { vertical-align: 100%; }
</style>

inline元素下方可能会有点间隙

来看一个例子。一个常见的情况就是你尝试将列表的li元素垂直对齐。

1
2
3
4
5
6
7
8
9
10
<ul>
<li class="box"></li>
<li class="box"></li>
<li class="box"></li>
</ul>
<style type="text/css">
.box { display: inline-block;
/* size, color, etc. */ }
</style>

正如你所看到的,li元素是对齐baseline的。baseline的下方会有一部分留给文字的空间。这便会产生小缝隙。那么解决方案是什么?只要改变line-box的baseline的位置就好了,比如把li元素的vertical-align:middle.

1
2
3
4
5
6
7
8
9
10
11
12
<ul>
<li class="box middle"></li>
<li class="box middle"></li>
<li class="box middle"></li>
</ul>
<style type="text/css">
.box { display: inline-block;
/* size, color, etc. */ }
.middle { vertical-align: middle; }
</style>

inline元素之间的间隙会破坏布局效果

这主要是inline元素自身的问题。但是因为这是让vertical-align发挥作用的必要因素,所以了解它也是不错的。

这个间隙主要是来源于inline元素之间的空格。所有空白都会变成一个空格。这个空格可以通过该方法反映,比如如果你想让两个inline元素紧挨着,然后给他们的宽度分别设置为50%,这里并没有足够的空间存放两个宽度为50%的元素以及一个空格。所以一行便会被破坏成两行。为了移除空隙,我需要移除空格,解决方案如下:

1
2
3
4
5
6
7
8
9
10
11
12
<!-- 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>
<style type="text/css">
.half { display: inline-block;
width: 50%; }
</style>

Vertical-align揭秘

是的,对于你来说了解这些规则并不复杂。如果vertical-align不起作用,那么想想看这两个问题: