1

平时遇到vertical-align时候会有各种抓狂的时刻,调来调去虽然也弄好了但是心里面一直很虚,因为一直没有透彻理解过这个属性,搜索时候发现了一篇很棒的文章,算是半翻译吧,把这个属性好好总结一下。

引言

说起vertical-align大家都知道他是用在对相邻的文字和内联元素上,比如常见的将一个图标和相邻的文字居中对齐。但是当你不了解他的机制时候经常会让你抓狂,因此我们一次来攻克这个规则的玄机。

vertical-align是如何作用于行框中的内联元素

vertical-align用来对齐内联级元素,也就是display值为inlineinline-block或者inline-table(这个通常不考虑了)的元素。
inline元素也就是包含文字的最基本的标签如span
inline-block,顾名思义就是表现为行内的块级元素,可以设置宽度、高度和间距边框等。

内联级元素以行的形式相邻排列在一起,一旦有更多的内联级元素超过当前行能容纳的长度,就会在其下面产生一个新行。也就是我们常说的line box(行框),行框包含其行内所有内容。不同尺寸的内容意味着行框也有不同的高度。下图就用红线标注了三个行框的top和bottom。
图片描述

行框勾勒出了这些内容占据的区域,其中vertical-align就负责对行框中的各个元素进行对齐。问题来了,这些元素都是相对于哪些线对齐呢?

关于baselines, tops, bottoms你要知道的

垂直对齐最关键的参考线就是元素的基线baselines,在某些情况下元素的包含盒的顶端线tops和底端线bottoms也变得很重要。下面就分别看看各种类型元素的baselines、tops和bottoms。

inline元素

图片描述

如图所示,有三行不同标线的文字。红线代表line-height的顶端和底端;绿线代表文字字体的高度,这里需要注意的是其实文字内容区域实际占据的高度并不严格等于字体高度而是由font-sizefont-family共同决定的,特殊情况就是宋体字体下,实际内容区域高度就等于字体高度。但是这点差距一般情况下也无伤大雅,我们就假定用字体高度来表示内容区域高度即可;蓝线代表基线baseline,可以理解为字母x的底边缘线,就是比字体高度中心位置低大约1/2的x高度。

左图的行高设置为同字体高度相同,因此红线和绿线重合了。中图行高设置为两倍字体高度,行间距分为上下两部分等分了一个字体高度,即相对于绿线分别向上向下半个字体高度。右图行高设置为字体高度的一半,即相对于绿线分别向下向上四分之一字体高度。

inline-block元素

图片描述

从左到右分别是包含流内in-flow内容的inline-block元素、包含流内in-flow内容的inline-block元素同时设置了overflow:hidden和一个不含流内内容但是有高度的inline-block元素。红线表面了inline-block元素的外边距margin边界,图中盒模型黄色是border,浅绿是padding,浅蓝是content;蓝线表示inline-block元素的baseline。

可以得出,inline-block元素外部边缘线就是margin-box的顶端和底端边界,也就是图中的红线。基线的规则判断比较复杂,可以简单记为:若不包含内容或者overflow属性不为visible以外的值,则其基线就是底部边缘线,也就是margin-box的底端边界。

行框line box

图片描述

上图用几根线很清晰的表现出了行框的结构。绿线代表的是行框中text box,这里称作字框的的顶端和底端线。同时用灰色背景高亮了文字的区域。

行框顶线和其中最高的元素对齐,行框底线和其中最低元素对齐。

行框的基线在草案中并未作出精确的定义:

CSS 2.1 does not define the position of the line box's baseline. — the W3C Specs

这可能是最让人困惑的地方了,他的意思就是行框的基线就位于能同时满足所有其他vertical-align和最小化行框高度的位置。这样说还是很抽象,但是有个可以让其具象的方法,也就是像图中那样在行框的最前面加上一个x字符,这个字符就是对齐了基线baseline

围绕着这个基线的绿线也就是字框,可以理解为行框中一个没有任何对齐方式的内联元素,其高度等于字体的高度(如前面说的一样,不考虑字体的因素了)。草案中把我们这里用字符x模拟的看不见字框称作strut,张鑫旭大神在其博客中称为幽灵空白节点。
总结下,行框就是vertial-align发挥作用的区域,他包括一个基线、字框、顶线和底线。每个内联元素也有其自己的基线,顶线和底线。

vertical-align对齐baselines,tops,bottoms

图片描述

用上图表示各种对齐方式的形式。

  • baseline: 内联元素的基线正好位于行框基线上。
  • sub:内联元素的基线位置低于行框的基线。
  • super:内联元素的基线位置高于行框的基线。
  • <percentage>:内联元素的基线相对于行框基线移动相应于行高百分比的数值。
  • <length>:内联元素的基线相对于行框基线移动某个绝对数值。
  • middle:居中对齐可以着重讲一下,他的意思就是内联元素的顶线和底线的中线与行框的基线向上偏移二分之一个x-height,可以近似理解为字符x中间交叉点。

图片描述

  • text-top: 内联元素顶线与行框的字框顶线对齐。
  • text-bottom: 内联元素底线与行框的字框底线对齐。

图片描述

  • top: 内联元素顶线与行框顶线对齐。
  • bottom: 内联元素底线与行框底线对齐。

图片描述

案例分析:vertical-align为何如此表现

图片和文字对齐是前端开发者最常见的场景了,虽然现在很多都用字体图标或者用一些组件很多情况下不需要考虑对齐了,但是当你自己写的时候还是务必要搞清楚这个基本原理。下面看这个例子。

居中图标

一个图标相邻有一行文字,如果让文字和图标居中对齐,大部分人会对图标使用vertical-align: middle,但是你会发现好像并未真正的居中对齐:

图片描述

上图两边的对比很明显,左边文字有些许偏上,右边才是完美的居中对齐,两边的代码如下:

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

这是为何呢?

画出辅助线来看一下:

图片描述

很明显了,左图的文字是默认基线对齐的,以x的中线位置分割带有ascender的文字会偏上一些。

而右图对文字用span标签包裹然后对整个文字内容区域也使用middle,这样其基线位置相对于行框基线会稍微下沉一些,这样就与图标居中对齐了。

行框基线位置的移动变化

前面提过,行框基线的位置会被行内所有元素所影响,而大多对齐方式都是和行框基线有关的,因此假如行框基线发生了变化,整个布局就会出现变化。

举例说明:

  • 假如有一个很高的内联元素高度高于了之前行框高度时候,看看此时行框占据的空间如何变化,下图左边高的内联元素text-botttom对齐,右边text-top对齐。可以看到行框基线和顶线,底线的位置。

图片描述

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

内联元素下面的间隙

我们再来思考下经常遇到的一个场景:

图片描述

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

内联元素下面总会有一个间隙,就是因为幽灵空白节点在作怪,内联块的基线现在就是底边缘线和字框的基线对齐后,间隙就是字框基线下的descender,为避免这种问题其实就是改变行框基线的位置即可,比如topmiddle都可。

图片描述

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

总结

以后再碰见对齐的问题,只需要搞清楚两个问题就可以了:

  • 行框的基线、顶线、底线在哪里?
  • 内联元素的基线、顶线、底线在哪里?

Alee
291 声望8 粉丝

既然路走偏了,那就重新开始吧。