原文地址: Vertical-Align: All You Need To Know
我经常需要垂直对齐元素。
CSS提供了一些可能的方法。有时我用 float
解决它,或使用 position: absolute;
,还有时甚至要恶心地手动添加 margin
和 padding
。
我不是真的喜欢这些解决方案。 float
只能对准在其顶部而且需要手动清除。绝对定位将元素从文档流中抽离出来,这样它们就不再能撑开(影响)其包含块。使用固定的 margin
和 padding
则很容易被微小的变化所破坏。
但这里有另一个方法: vertical-align
,我认为它值得得到更多的信赖。从技术上讲,使用 vertical-align
布局是一种hack方式,因为它不是为这个原因发明的。它的存在是为了对齐文本和文本旁的元素。尽管如此,你也可以在不同的上下文中使用 vertical-align
来非常灵活和精准地对齐元素。优点是元素的大小不需要知道,且留在文档流中,所以其他元素(包含块)可以响应布局尺寸的改变,这使它成为一个有价值的选择。
但是, vertical-align
有时会相当卑鄙,并使你感到沮丧。在工作中似乎有一些神秘的规则,例如,这种情况经常发生,被改变了 vertical-align
的元素并没有改变它的对齐方式,但同一行的其他元素却改变了!我有时仍然被 vertical-align
拖到黑暗的角落并撕扯头发。
不幸的是,大部分关于这个的教程都有点浅,特别是如果我们要使用 vertical-align
来布局。大多数人专注于尝试垂直对齐元素的一些误解,他们给出基本的介绍,并解释在非常简单的情况下如何对齐元素,但没有解释棘手的部分。
所以,我给自己定了个目标: 彻底弄清楚 vertical-align
的行为 ,不留下历史问题。我最终通过W3C的 CSS 规范 和试验一些例子得到了结果——这篇文章。
vertical-align
用来调整 inline 级元素。这些元素的 display
属性为:
inline元素基本上是文本的包裹标签。
inline-block元素正如它们的名字:住在行内的块元素。他们可以有一个width和height(也可能由它内部内容撑开),以及padding、border和margin。
inline元素逐个从左到右地被放置在一行内。一旦有更多的元素加入,使当前行无法放下,一个新的行就会在下方产生。所有这些行有所谓的 line box ,其中包含所有的内容。不同尺寸的内容意味着不同高度的line box。在下图中,line box的顶部和底部是红色线表示的:
line box描绘出了我正在试验的区域。在这些line box中,属性 vertical-align
是负责调整单个元素的。 所以,元素对齐跟什么相关?
垂直对齐最重要的关注点是要对其元素的baseline。在某些情况下,元素的包围盒的顶部和底部的边缘也变得很重要。让我们来看看每种类型的元素baseline和外边缘在哪里:
在这里你看到三行的文本相邻排布。行高的顶部和底部是由红线表示的,字体的高度是由绿线来表示的,而baseline是由蓝线表示的。在左边,文本具有一个与字体大小相同的行高,绿色和红线在上下都重叠了。在中间,行高(line-height)是字体大小的两倍大。在右边,行高是字体大小(font-size)的一半。
inline元素的外边缘跟自己line-height的顶部和底部边缘对齐,如果line-height小于font-size的话也不会改变。所以,在上面的图中的行的外边界是红线。
inline元素的baseline字符底部所坐的线,就是图中的蓝线。粗略地说,baseline是在font-size中间下面的一个地方,看看W3C规范的 详细定义 。
从左到右,你看到的是一个拥有 流 内容(一个“c”)的inline-block元素,一个拥有流内容且 overflow: hidden;
的inline-block元素和一个没有流内容的inline-block元素(但内容区域有一个高度)。margin的边界由红线表示,以及黄色的border,绿色的padding和蓝色的内容区域,蓝线是每个inline-block元素的baseline。
inline-block元素的外边缘是其 margin-box 的顶部和底部边缘,即图中的红线。
inline-block的baseline取决于元素是否具有流内容:
你已经看过了这张图上面的设置,这一次,我画出了line box的text box的顶部和底部边缘(绿线,下文也是)以及baseline(蓝线)。我还给该区域的文本元素添加了一个灰色的背景来强调他们。
该line box具有一个与该行最顶的元素的顶部边缘对齐的 顶部边缘 和一个与该行最底的元素的底部边缘对齐的 底部边缘 ,即是上面图中的红色线所表示的盒子。
line box的baseline是可变的:
CSS 2.1并没有定义line box的baseline. —- W3C规范
这可能是使用vertical-align时最令人困惑的部分。意思是,baseline的位置要在满足所有其他条件,如vertical-align设置和最小化line box的高度的前提下来决定,是方程中的一个自由参数。
由于line box的baseline是不可见的,无法直观感知。但是,你可以很容易地让它变得可见。通过在行的开头加一个字符,比如上图中的字母“×”。如果这个字符没有以任何方式进行对齐,则默认情况下其底部将坐在baseline上。
在其baseline的基础上,line box有一个称为 text box 的东西。该text box可以简单地被认为是line box中一个没有任何对齐的inlne元素。它的高度等于它的父元素的font-size。因此,text box只包裹住line box中的无格式文本,由上图中的绿线表示。因为这个text box位置基于baseline,所以它会随baseline移动。(注:此text box在W3C规范中称为 strut )
唉,这是最难的部分。现在,我们拥有了分析原理的所有前提条件,让我们迅速总结最重要的事实:
从上面的列表总结出来的使用 vertical-align
的要点中,我设置了几种关系样例。
这2种情况也可以以相对于line box的baseline对齐的方式展现出来,因为text box的位置是由baseline确定的。
正式的定义 可以在W3C规范中找到。
我们现在可以在特定情况下来研究一下垂直对齐,特别是有可能会出错的情况。
下面这个问题曾经烦扰了我:我想把图标与旁边的一行文本垂直居中对齐,只是给图标设置 vertical-align: middle;
似乎并没有如设想的一样居中。看看底下这个例子:
<!-- left mark-up --> <spanclass="icon middle"></span> Centered? <!-- right mark-up --> <spanclass="icon middle"></span> <spanclass="middle">Centered!</span> <styletype="text/css"> .icon { display: inline-block; /* size, color, etc. */ } .middle{ vertical-align: middle; } </style>
下面的图还是上面的例子,但我画了一些之前提到过的辅助线:
这揭示了我们的问题。左边的x和Centered?没有设置对齐属性,所以它坐在baseline上。重要的是,用 vertical-align: middle;
对齐灰色方块(即图标)后,我们把它的中点与line box的baseline加上x高度的一半处对齐了(图中黄线),然而由于Centered?中部并不在黄线上,所以就显得高于灰色方块(图标)。
在右边,我们进一步把文字中点也对齐了,这使得文本的baseline略微往line box的baseline下方偏移了一点。最终得到了完美的结果。
这里有一个使用 vertical-align
常见的盲点:line box baseline的位置会被行内所有元素所影响。让我们假设这种情况,一个元素以使得line box baseline必须移动的方式进行对齐。由于大多数垂直对齐方式(除了顶部和底部)与baseline相关,这会导致该行中的所有其他元素移动。
一些例子:
vertical-align
对它就没有效果,上下都没有空间让它移动,所以line box的baseline为了满足对齐关系必须移动。在下图中,短方块设置了 vertical-align: baseline;
,左例中高方块的垂直对齐方式是 text-bottom
,而在右边中是 text-top
。你可以看到baseline与短方块始终在一起。
<!-- left mark-up --> <spanclass="tall-box text-bottom"></span> <spanclass="short-box"></span> <!-- right mark-up --> <spanclass="tall-box text-top"></span> <spanclass="short-box"></span> <styletype="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
的其他值来对齐高方块时也有相似的表现。
vertical-align
为 bottom
(左图)和 top
(右图)也会使baseline移动。这十分奇怪,因为这应该与baseline并无关系。
<!-- left mark-up --> <spanclass="tall-box bottom"></span> <spanclass="short-box"></span> <!-- right mark-up --> <spanclass="tall-box top"></span> <spanclass="short-box"></span> <styletype="text/css"> .tall-box, .short-box{ display: inline-block; /* size, color, etc. */ } .bottom { vertical-align: bottom; } .top { vertical-align: top; } </style>
<!-- left mark-up --> <spanclass="tall-box text-bottom"></span> <spanclass="tall-box text-top"></span> <!-- mark-up in the middle --> <spanclass="tall-box text-bottom"></span> <spanclass="tall-box text-top"></span> <spanclass="tall-box middle"></span> <!-- right mark-up --> <spanclass="tall-box text-bottom"></span> <spanclass="tall-box text-top"></span> <spanclass="tall-box text-100up"></span> <styletype="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>
这是在试图垂直对齐 li
元素时很常见的情况。
<ul> <liclass="box"></li> <liclass="box"></li> <liclass="box"></li> </ul> <styletype="text/css"> .box{ display: inline-block; /* size, color, etc. */ } </style>
正如你所看到的, li
元素坐在baseline上,而baseline下面是留给下标的空间,这导致了空白间隙。解决的办法很简单:将baseline上移一点。例如用 vertical-align: middle;
来对齐 li
元素。
<ul> <liclass="box middle"></li> <liclass="box middle"></li> <liclass="box middle"></li> </ul> <styletype="text/css"> .box { display: inline-block; /* size, color, etc. */ } .middle{ vertical-align: middle; } </style>
这种情况不会发生在具有文本内容的inline-block中,因为 内容已经使baseline往上移动了 。
这主要是inline级元素本身的一个问题,但因为vertical-align的需要它们,所以还是知道比较好。
你在前一个例子的 li
中已经看到了这个间隙,它来自于你的html中inline元素之间的空白符号(空格、回车、换行)。html中多个连续的空白符号会在显示时被合并为一个空格,也就是图中的间隙。如果我们想把两个inline元素水平放置并给它们设置 width: 50%
,就无法跟额外的那个空格放在同一行,所以就会换行破坏布局(左图)。为了消除这个间隙,我们需要去除空白符号,比如使用HTMl注释(右图)。
<!-- left mark-up --> <divclass="half">50% wide</div> <divclass="half">50% wide... and in next line</div> <!-- right mark-up --> <divclass="half">50% wide</div><!-- --><divclass="half">50% wide</div> <styletype="text/css"> .half{ display: inline-block; width: 50%; } </style>
就是这样,当你知道规则那就不复杂了。所以如果 vertical-align
并没有如预期发挥作用,仔细思考下面两点,就能得到结果:
源于 Warrior!博客