10

原文:Vertical-Align: All You Need To Know

通常我都有需要垂直对齐在一排上一个接着一个的元素。
CSS提供了很多种可能性。有时,我听过float来解决问题,有时使用position:absolute,有时甚至会通过添加margin或者padding属性这种使代码变得比较脏的方式来达到目的。

我一点也不喜欢上面这些解决方案。浮动仅仅是顶部对齐并且需要手动清除浮动。绝对定位让元素脱离文档流,所以他们不再影响他们的周边元素。而使用固定值的外边距和内边距会出现细微的差别。

但是这里还有另外一种玩法:vertical-align。我认为他值得更多的赞誉。OK,技术上来说使用vertical-align布局是一种hack的手段,因为他并不是因为实现我们上面那种需求发明的。尽管如此,你也可以在不同的上下文中使用vertical-align来灵活细致的摆放元素。不需要知道元素的大小。元素在文档流中,也能感知其他元素尺寸的改变。这些都让vertical-align变成了更有价值的选项。

独特的vertical-align

但是,vertical-align有时也是个卑鄙小人。使用它工作也会使人有点沮丧。在使用它工作时,这里会有一些令人难以理解的规则。例如,它可能会发生这样的情况,一个元素改变了他自己的vertical-align的值,而他的位置却一点都没有改变,改变的却是同行的其他元素。我仍然会一次一次被拉入这种奇怪的现象,使我苦恼。

不幸的是,大多数资料都讲述的比较浅显。特别是,当我们想使用vertical-align布局时。他们对最基本的属性进行了介绍并且在简单的例子中解释元素是如何在一行中进行摆放的。但是,他们并没有去解释它令人感到奇怪的部分。

所以,我的目标是使自己彻底弄清楚vertical-align到底是怎么回事。最后消解我疑问的是W3C's的CSS specifications和标准中演示的一些例子。答案就在文章中。

所以,让我们来解决掉这场关于规则的游戏吧。

使用vertical-align的要求

vertical-align被用于inline-level elements。这些元素的display属性如下

  • inline

  • inline-block

  • inline-table(这篇文章中,并不考虑这种情况)

基本的inline元素都是标签裹着文字。

inline-block元素:是在行内中的块级元素。他们可以有宽度和高度(通常情况下,这取决于他们的内容)。同样也有padding,margin,border。

行内级元素在一行中一个挨着一个。一旦,这些元素超出了他们的所在行,一个新行便会建立在它下方。这里的每一行就叫做line box。每一行不同尺寸的元素意味着line box不同的高度。在下面的例子中红线代表line box的顶部和底部。

line box寻找我们放置元素的轨迹。在这些line boxes里面vertical-align属性负责摆放单独的元素。所以什么样的元素被用来对齐了呢?

关于基线和边界线

在一行中垂直摆放元素,最关键的参考点是参与的元素的基线。在一些例子中,被关闭盒子的顶部和底部也变得非常重要。让我们一起看看基线和外边缘是如何参与到不同类型的元素中的。

Inline Element

clipboard.png

这里有三行挨着的文字。顶部和底部的边缘线是行高的边缘线,它们都是红线。字体的高度被绿线包裹着。蓝色的线代表基线。在最左边,文字的行高和字体本身的高度一样高。绿线和红线发生了重叠。在中间,行高是字体大小的2倍高。在最右边,行高是字体大小的一半高。

inline element的外边缘的位置,取决于他们的行高的顶部与底部边缘。如果行高的高度比字体的高度小。那么,红色的外边缘的位置就如上面提到的一样。

inline element的基线,在字符放置的位置。在图中,用蓝线表示。初略的讲,基线的位置一般是在字体高度一半以下的位置。可以看看W3C的标准对它的定义,detailed definition

Inline-Block Element

clipboard.png

从左边到右边的图你可以看到:最左,一个在in-flow内的行内块级元素。中间,一个在文档流中的inline-block元素并且带有overflow: hidden属性的元素。最右,不在文档流中的inline-block元素(但是内容区域有高度)。margin的边界线指向红线,border是黄色的部分,padding是绿色的部分,内容区域是蓝色的部分。每一个inline-block元素的基线都用蓝线表示。

inline-block元素的外边缘 是它的margin-box的顶部和底部。在图上,用红线来表示。

inline-block元素的基线 依赖于元素是否是文档流中的元素。

  • 假设内容在文档流中,inline-block元素的基线是普通流中的最后一个内容元素的基线(最左的例子)。对于最后一个元素的基线根据它自己的规则来找寻。

  • 假设内容在文档流中,但是元素有overflow属性除了属性值为visible这种情况,基线在margin-box的底部(最中间的例子)。所以,这等同于在inline-block元素的底部边缘。

  • 假设内容不在文档流中,同样的,margin-box的底部边缘也是基线的位置所在。(例子在最右边)

Line Box

clipboard.png

你已经看到了上面元素的放置。这次,我画了line box的text box顶部和底部的线(如图中的绿线,更多内容看下文),基线如图中蓝色的线。我在文字元素上加了灰色的背景进行强调。

line box的顶部边缘与最顶部元素的顶线对齐,底部边缘与最底部元素的底线对齐。图中红线代表的就是line box。

line box的基线是多种多样的

“CSS2.1 并没有对line box基线的位置有明确的定义。-W3C Specs

当使用vertical-align工作时,这可能是最令人迷惑的部分。这意味着,基线的位置改变是为了满足像垂直对齐和降低line box的高度这样的需求的。也就是说,在方程中,这是一个自由的参数。

因为line box的基线是不可见的,因此它的位置并不会那么明显。但是,你可以容易的使它变得可见。只需要在一行的开头加上一个字母,就像我在图中添加的字母"x"。如果这个字母并没排在一条直线上,那么x坐落的位置就是基线的位置。

在line box基线的周围有text box。text box可以简单的被认为是在line box中的行内元素。它的高度等同于它的父元素的文字大小。因此,text box仅仅只是围绕着line box的非格式化文字。这个盒子在上图中是指向绿线的位置。text box与基线的位置相关联,它随着基线而移动。

哦,现在是最难的部分。现在,我们把知道的所有事,都表现在了上图中。让我们快速的总结最重要的部分:

  • 这里有一个区域被叫做line box。这是元素垂直对齐的区域。它有一个基线和一个text box以及顶部与底部边缘。

  • 这里有inline-level元素。他们是被对齐的对象。他们有基线以及顶部与底部边缘。

Vertical-Align的值

通过使用vertical-align的参考点,也就是上文提到的基线和边界线,将元素放置在合适的位置。

相对于line box的基线放置元素的基线

clipboard.png

  • baseline: 元素的基线刚好放置在line box基线的上。

  • sub: 元素的基线放置在line box基线的下面。

  • super: 元素的基线放置在line box的基线的上面。

  • <percentage> 元素的基线移动相对于line box的基线通过相对于line-height的百分比的值来移动。

  • <length>: 元素的基线相对于line box的基线通过绝对值的大小进行移动。

相对于line box的基线放置元素的外边缘

clipboard.png

  • middle: 元素的顶部与底部边缘的中点相对于line box的基线移动x-height的一半的位置对齐。

相对于line box的text box放置元素的外边缘

clipboard.png

  • text-top: 元素的顶部边缘被放置在line box的text box的顶部边缘

  • text-bottom: 元素的底部边缘被放置在line box的text box的底部边缘

相对于line box的外边缘放置元素的外边缘

clipboard.png

  • top: 元素的顶部边缘被放置在line box的顶部边缘

  • bottom: 元素的底部边缘被放置在line box的底部边缘

在W3C标准中的定义

Vertical-Align的行为就如它的表现一样

现在,我们可以在具体的例子中看垂直对齐。特别是,处理一些比较容易出现差错的情况。

放置icon在中间

这是一个一直以来都令我烦恼的事情:我有一个icon我想将它放置在一行文字的中间。只只仅仅将icon设置vertical-align: middle,看起来似乎不是一个安全的方法。看下面的例子。

clipboard.png

<!-- 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>

这里也是上面这个例子,不过,这次我加了一些你从上文了解到的辅助线,

clipboard.png

这有助于帮我们理解。因为在左边的文字并没有对齐元素,而是被放置在基线所处的位置。通过使用vertical-align:middle来对齐盒子,我们将只是将盒子放置在了小写字母的中间。所以,出头部分的字母出现在顶部。

在右边,我们将全部文字区域放置在垂直方向的中点。通过移动text的基线到line box的基线的下方来实现。结果是文字完美的居中于紧挨着的icon。

移动line box的基线

当我们使用vertical-align工作时,容易遇到的一个共同的问题:line box的基线会被在同一行中所有元素影响。让我们猜一猜,一个元素有可能在这样被对齐的,通过移动line box的基线。因为大多数垂直对齐(除了顶部和底部)都是相对于基线,这也会导致在同一行的的所有元素的位置都会被调整。

一些例子:

  • 如果在一行中,有一个高的元素占据了整行的高度,vertical-align对它不会有任何影响。在它的顶部上面与底部下面没有任何多余的空间可以移动它。为了实现它相对于line box基线的对齐,line box的基线必须移动。矮一点的盒子设置了vertical-align: baseline。在左边,高盒子根据text-bottom对齐。在右边,根据text-top对齐。你可以看到矮盒子的随着基线位置的改变而改变。

clipboard.png

<!-- 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的值分别设为leftbottom移动基线。而这很奇怪,因为基线本不应该参与。

<!-- 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>

clipboard.png

<!-- 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>
  • 在一行中,放置两个大体积元素并且移动基线垂直对齐他们以此满足同时对齐。line box的高度被调整(左边的例子)。添加第三个元素,它并有跑到line box的边缘因为它的对齐属性,既没有影响line box的高度,也没有影响line box基线的位置(中间的例子)。如果它真的跑到了line box的边缘,我们的前两个盒子会被往下推(最右的例子)。

clipboard.png

<!-- 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>

解开Vertical-Align的神秘面纱

当你知道了规则,这就没那么复杂了。如果vertical-align并没有按照你想的那样行动,问自己两个问题:

  • line box的顶部与底部边缘和基线在哪里?

  • inline-level元素的顶部与底部边缘和基线在哪里?

这将解决你的问题。


阿花和猫
2.3k 声望138 粉丝