我经常需要让元素在垂直方向上对齐。
CSS
提供了很多可用的方法。有时候我用 float
解决问题,有时候使用 position: absolute
,而有些时候呢,甚至通过手动添加 margin
与 padding
这样污染代码的方式来实现。
我实在不喜欢这些解决方法。浮动元素仅在顶部对齐并且需要手动清除浮动。绝对定位使得元素脱离文档流,所以它们将再也无法影响周围元素。还有,使用固定 margin
和 padding
值的话,极小的改变都会立即破坏现有的布局。
但是,这里还有另一个角色: vertical-align
。我认为它应当受到重任。好了,确切地说,使用 vertical-align
来布局是一种 hack
行为,因为它不是为此而生。它用来排列文本和文本旁边的元素。尽管如此,你还是可以在不同的上下文中用 vertical-align
来灵活地、精细地排列元素。元素的大小并不需要知晓。元素会继续在文档流中,所以其他元素可以据此改变它们的尺寸。这些优势让它成为了有价值的选择。
vertical-align 的独特性
但是,某些时候 vertical-align
是个十足的混蛋,用它解决问题可能会让人沮丧,似乎有一些难以理解的规则在起作用。比如,可能会出现这样的情况,你改变了 vertical-align
属性值的的元素压根没有改变对齐方式,但同一行的其他元素却有变动。我苦恼地撕扯着头发,仍不时地陷于 vertical-align
的死角。
不幸的是,大部分有关于 vertical-align
的资料都稍显粗浅,尤其是我们想藉此进行布局的时候。它们都集中精力在这样一个误区,试图让一个元素中的所有东西垂直对齐。它们给出了这个属性的基础介绍,以及在特别简单的情况下元素如何对齐,但并未解释棘手的那部分。
因此我给自己设立了一个目标,要一劳永逸地阐述清楚 vertical-align
的行为表现,最终通过研究 W3C 组织的 CSS 规范文档 并且演练一些例子完成了。成果就是这篇文章。
所以,就让我们来讨教一下这场游戏规则吧!
使用 vertical-align 的条件
vertical-align
被用来对齐 内联级元素 ,指这些元素—— display
属性计算值为
- inline
- inline-block
- inline-table(本文中不作考虑)
inline 元素基本上是包裹文字的标签。
inline-block 元素人如其名:披着 inline
外套的块元素。就像它拥有 padding
、 border
、 margin
一样,它同样可以拥有 width
和 height
(可能由其自身内容所决定)。
内联级元素会在各行一个挨着一个排列开来,如果当前行有太多元素放不下的时候,新的一行会在它下面产生。所有这些行都拥有一个行盒,它包裹着该行所有内容。不同大小的内容意味着行盒拥有不同的高度。在下面的插图中,行盒的顶部和底部,都以红色线条表示。
这些行盒描绘出了我们可以作用的区域。在这些行盒中, vertical-align
属性的职责是对齐一些独立的元素。 那么问题来了,元素都是相对于谁来对齐的呢?
关于基线和外边缘
垂直对齐最重要的参考点就是所涉及元素的基线,某些情况下元素的盒模型的上下边缘也很重要。就让我们来看看涉及到的各个种类的元素的基线和外边缘在哪儿。
inline 元素
这里你可以看到相连文字的三条线,行高的上下边界用红色线条,文字的高度用绿色线条,基线用蓝色线条标注出来了。左边,文字行高被设置为文字大小的高度;中间,文字行高是文字大小的两倍;右边,文字行高是文字大小的一半。
行内元素外边缘 与它行高的上下边缘对齐,如果行高小于字体的高度也无所谓,所以,在上图中外边缘是红色线条。
行内元素基线 就是字符坐着的那条线,在图中是蓝色线条。严格来说,基线就是处在文字高度中间以下的某处。好好看看 W3C 规范寻求一下详细定义 。
inline-block 元素
从左到右你可以看到三个内联块元素,左边的包含着文档流内的内容(一个‘c’),中间的除此以外还添加了 overflow: hidden
,右边的没有文档流内的内容(但内容区域还有高度)。margin
边界用红色线条标注, border
用黄色, padding
用绿色,内容区域用蓝色。每个 内联块元素的基线是用一条蓝色线条展示的。
内联块盒的外边缘 是 margin
盒的上边缘和下边缘。他们在图中是红色线条。
内联块盒的外边缘 取决于元素是否拥有文档流内的内容:
- 在拥有流内内容的情况下,内联块元素的基线是最后一个流内内容元素的基线,这个元素的基线根据其自身规则来计算。(左边例子)
- 在拥有流内内容并且
overflow
计算值不是visible
的情况下,基线是margin
盒的下边缘。这样,它就和内联块元素的下边缘一样了。(中间例子) - 在没有拥有流内内容的情况下,基线同样是
margin
盒的下边缘。(右边例子)
line box
这种标注你们在上面就已经看过了,这次我同样画了行盒中文本框的上下边界(绿色)和基线(蓝色)。我还给文本元素设了灰色背景来高亮显示它们。
行盒的上边界与最高元素的上边界对齐,下边界与最低元素的下边界对齐。上图中红线标注的框就是。
行盒的基线 是可变的:
CSS 2.1没有定义行盒基线的位置. — W3C 工作组
当我们使用 vertical-align
的时候,这可能是最让人疑惑的部分了。基线会被放置在任何需要的地方,用以达成像 垂直对齐 和 最小化行盒高度 的情况。它是等式中的自由变量。
行盒的基线是不可见的,它不能立马显示出它在哪里,但是可以很轻易地让它可见。在行的开头添加一个字符,比如我在例子中添加了’x’。如果这个字符没有以任何方式对齐的话,它将默认基线对齐。
行盒中,在基线周围有一个叫做 文本框 的存在。文本框可以被简单地当成行盒中的一个没有任何对齐方式的内联盒。它的高度等于它的父元素的字体大小。因此,文本框只包含行盒的非格式化文本,它在上图中被绿色线条标注。由于文本框与基线绑定在一起,当基线移动时它也移动。(这里所说的文本框在 W3C 工作组被叫做 strut )
喔,这就是最难的部分,通过各个角度观察我们已经知晓了所有事情。让我们赶紧总结一下最重要的事实:
- 这里有个叫做
line box
的区域,在这里,会有垂直对齐。它拥有一条基线,一个文本框,以及上下边界各一条。 - 内联级元素,是会对齐的对象。它们拥有一条基线,上下边界各一条。
vertical-align 的值
上面那个列表中最后一句话提及的参考点,因为 vertical-align
而有了关联。
元素的基线与行盒的基线
- baseline:元素基线正好就在行盒基线上
- sub:元素基线在行盒基线下方
- super:元素基线在行盒基线上方
:元素基线根据行盒基线位置移动 line-height
*<percentage>
:元素基线根据行盒基线位置移动绝对的长度
元素的外边界与行盒的基线
- middle:元素上下边界间的中心点对齐在行盒基线加上一半x-height
元素外边界与行盒的文本框
相对于该行框的基线对齐,我们还可以列出如下这两种情况,因为文本框的位置由基准确定。
- text-top:元素上边界与文本框上边界对齐
- text-bottom:元素下边界与文本框下边界对齐
元素外边界与行盒外边界
- top:元素上边界与行盒上边界对齐
- bottom: 元素上边界与行盒下边界对齐
当然,W3C 规范中就有 正式定义 。
为何 vertical-align 如此变现呢
我们可以在特定的情境下近距离观察下垂直对齐,尤其是,我们在处理某些bug的情况下。
围绕一个图标
一直困扰着我的一个问题是这样的:我有一个图标,我想让他紧邻一行文字居中对齐,我给它加上了 vertical-align: middle
但似乎没有以令人满意的方式居中。来看看这个例子:
|
|
这里我再次展示一下这个例子,不过我画了一些辅助线,上面已经讲过了。
它揭示出了我们的问题的一些情况。因为左边的文字压根没有对齐,实际情况是,使用 vertical-align
我们会把盒子对齐在没有延伸(1/2 的x-height)的小写字母的中心点。所以,有延伸的字符会伸出顶部。
在右边,文字与icon都对齐于一个中点,文字的基线稍微下移,位于行盒的基线的下方。结果是很好的达到了icon与文字对齐的效果。
行盒基线的移动
当我们使用 vertical-align
时,都有这样一个坑:行盒基线的位置受该行所有元素的影响。假设,一个元素以某种方式对齐了,行盒基线受影响移动了,由于大部分元素都已经根据这条基线垂直对齐完毕,最后的结果就是其他所有的元素都需要调整位置。
一些例子:
一个很高的元素,其高度占满了整个行盒,那么
vertical-align
对其是没有影响的,在它的顶部和底部之外没有空间让其移动。但是为了满足它的对齐需求,行盒的基线会发生移动,左面的高元素的取值为text-bottom
,矮元素的取值为baseline
。右面的高元素的取值为text-top
,你会看到基线跳上去了。12345678910111213141516<!-- 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` 为其他值时,会有同样的表现。
甚至将
vertical-align
设置成bottom
和top
也会移动基线,这很奇怪,因为不该波及基线。12345678910111213141516<!-- 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>
此文为译文,现在不想翻译了,以后会把它补全,并放到译文目录下,vertical-align 章节自己会写一篇文章推上来。
就是这么任性。