你所不知道的vertical-align

有些东西我们经常用,但是我们却并不了解它的原理,所以一旦换了场景,好多东西就不知道该怎么用了。最近一直很纠结vertical-align到底是怎么回事,因为不管是垂直对齐元素,还是消除inline-block的缝隙也好,我们总能用到它,可是就是不知道为什么。抱着刨根问底的态度,今天在网上发现了一篇好文章,无奈是英文,博主不是什么英语大神,看到英语也头疼,但奈何好文章都是英文。那就来吧,翻译一下午,虽然是撕心裂肺,但是感觉受益匪浅,文中有翻译不当的地方还请各位看官多多指正。

小二,上原文链接:Vertical-Align: All You Need To Know


我们经常需要在竖直方向上对齐一些并肩排列的元素。
CSS为我们提供了一些实现方法。有时候我会使用float,有时候也用position:absolute,有时候甚至利用手动添加内外边距的方法。
我并不喜欢这些方法。浮动只能对齐它们的顶部,而且需要手动清除浮动。绝对定位会使元素脱离文档流,以致于它们就不能影响周围的元素了。再说说使用固定的内外边距,会突然导致微弱的改变。
这里还有另一种值得信任的方法,那就是vertical-align。从专业角度讲,使用vertical-align来进行布局是一种“黑科技”,因为发明它并不是出于这个目的。vertical-align是为了对齐文本和紧邻文本的元素。尽管如此,你也能在不同场景下使用vertical-align去进行灵活细微的元素对齐工作,而且你并不需要知道元素的具体尺寸。

Vertial-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中的block元素。它们可以设置宽高、内外边距、和边框。

inline级元素是一个挨着一个排在一行的元素。一旦一行放不下所有元素的时候,就会在下面创建一个新行。所有的行都有一个叫做line-box的东西,它是用来包裹这一行所有内容用的。line-box的高度是根据一行内所有内容的高度定的。如下图中所示:line-box的上下边沿用红线标出。(line-box的边沿平时是看不到的,画出来是为了方便读者理解)

Paste_Image.png

line-box规定出了我们对齐元素时的活动范围。在line-box中vertical-align负责每个元素的竖直对齐方式。那么问题就来了,什么是元素的对齐呢?

关于base-line(基线)和outer edge(外边沿)

理解垂直对齐最关键的一点在于理解相关元素的基线。还有一些场景中,元素的上下边沿也至关重要。现在就让我们看一下不同元素的基线和上下边沿到底在哪里?

inline元素

Paste_Image.png

从上图中你可你可以看到三种情况。行高的上下边沿用红线表示,绿线表示字体的高度,蓝线表示基线。左图中的行高和字体尺寸相同,所以红线和绿线重合在一起了。中间的图片,行高是字体高度的两倍。右图行高是字体高度的一半。
行内元素的外边沿就是它们的行高的上下边沿。即使行高比字体高度小也没有关系。因此,行内元素的外边沿就是上图中的红线。

行内元素的基线是字符下面的一条线。具体是图中的蓝线。大约的说,基线是字体正中线下面的一条线。想具体了解的请看W3C的详细说明

inline-block元素

Paste_Image.png

从左到右依次是:一个包含in-flow内容(一个字母C)的inline-block元素;一个包含in-flow内容并且设置了over-flow:hidden属性的inline-block元素;一个没有in-flow内容(但是设置了高度属性)的inline-block元素。三个例子都设置了margin属性,红线是margin的边缘,黄色部分是边框,绿色部分是内边距padding,蓝色部分是内容。蓝线是各个元素的基线。
inline-block元素的外边沿是它们的margin-box的上下边沿。也就是图中的红线。
inline-block元素的基线取决于元素是否有in-flow内容:

  • 包含in-flow内容的inline-block元素的基线是普通文档流中最后一个元素内容的基线(例如左图)。这最后一个元素的基线如何定位,那就看它是什么类型的元素了。
  • 包含in-flow内容而且设置了overflow属性(值非visible)的inline-block元素,其基线是margin-box的下边沿(例如中间的图)。所以它的基线就和自己的底边沿重合了。
  • 不包含in-flow内容的inline-block元素和上者相同,同样是底边线。

什么是line box?

![Paste_Image.png](http://upload-images.jianshu.io/upload_images/3068436-7140e215c7d96836.png?
imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
我为line-box中的text-box(下面会讲到)加上了上下边沿(绿线)以及基线(蓝线)。我也为文字元素加上了背景色(灰色)。
line-box有一个上边沿,它和行内最高的元素的上边沿对齐;同样也有一个下边沿和行内最低元素的下边沿对齐。这两个边沿也就是图中的红线。

line-box的基线是会变化的

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

这可能是最令人困惑的部分。这就意味着,line-box的基线是会根据一定的规则动态改变的。
由于line-box的基线是不可见的,所以你也不可能准确的说出它到底在哪里。但是能通过一个简单方法知道它在哪。只要在一行内容的行首添加一个字符就行,例如:我加了一个"x"(行首灰色的那个)。如果这个字符没有进行任何人为方式的对齐,那么它的基线就默认是在line-box的基线上。
line-box的基线周围的空间,我们可以称之为text box。你可以把text box看做是一个在line-box中没有进行任何对齐的inline元素。它的高度等于父元素的字体大小。因此text box是用于包裹line box中没有设置任何对齐的文字的。图中绿线之间的部分就是text box。由于text box是和基线紧密相连的,因此它会随着基线的移动而移动。(在W3C说明中,text box叫做strut)
现在我们知道了所有的预备知识点,让我们做一下总结:

  • 有一个叫做line-box的区域。各个元素是要在这里进行垂直对齐的。它包含一条基线,一个text box,一个上边沿,一个下边沿。
  • 这里还有一些inline级别的元素。这些都是我们要进行对齐的对象。它们同样有自己的基线,一个上边沿,一个下边沿。

Vertical Align都能使用哪些值

我们通过使用vertical-align可以把上面我们介绍的那些参考点(基线,上线边沿等)设置成确定的关系。
主要分一下两种情况:

将元素的基线相对于line-box的基线进行对齐

![Paste_Image.png](http://upload-images.jianshu.io/upload_images/3068436-e22cccdbf8b09e87.png?
imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

  • baseline:将元素的基线对齐到line-box的基线上;
  • sub:将元素的基线对齐到line-box基线下方;
  • super :将元素的基线对齐到line-box的基线上方;
  • <percentage> :元素的基线相对于line-box的基线进行确定距离的对齐,定位的距离和方向由设置的百分比数值决定,百分比是相对于line-height的。
  • <length>:元素的基线相对于line-box的基线进行确定距离的对齐,定位的距离和方向由设置的具体长度值决定。

将元素的外边沿相对于line-box的基线进行对齐

Paste_Image.png
  • middle:使元素的上沿和下沿的中点对齐到字母“x”的基线上边加上一半x的高度上(其实就是字母x的正中间)。

将元素的外边沿相对于line-box的text box对齐

Paste_Image.png

以下的两种情况,其实也是相对于line-box的基线的一种对齐,因为text-box的上下边沿也是取决于基线的。

  • text-top:元素的上边沿对齐到line-box的text-box的上边沿;
  • text-bottom:元素的下边沿对齐到line-box的text-box的下边沿。

将元素的外边沿相对于line-box的外边沿对齐

Paste_Image.png
  • top:将元素的上边沿和line-box的上边沿对齐;
  • bottom:将元素的下边沿和line-box的下边沿对齐;

为什么Vertical-Align会表现出这样的作用呢?

我们现在能够在具体的场景下更细致的研究vertical-align了。尤其是,我们将解决一些常见的问题。

使图标和文字居中

长久以来一直困扰我的一个问题:我想让一个图标和它旁边的文字居中对齐时,仅仅对图标使用vertical-align:middle似乎并没有真正的使两者居中对齐。
来看看下面这个例子:

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

下图还是这个例子,不过我为其添加了一些标注帮助理解,这些线都是我们之前已经讲过的:

Paste_Image.png

这样看是不是就有些明白是怎么回事了。因为左图中的文字没有经过对齐处理多以它默认是在text-box中的,它的基线和line-box的基线是重合的。通过给灰色方块设置vertical-align: middle;,我们的确把灰色方块的正中心对准了x字母的正中心,但是x字母的正中心并不是其所在的text-box的正中心,而是text-box中心稍稍的偏下了一点。
再看右图,我们把“center”包裹起来并为也设置vertical-align: middle;,“center”的基线就不再和line-box的基线重合了,而是稍稍下移了。结果图标和右边的文字就完美的居中对齐了。

line-box的基线是会移动的

使用vertial-align是的一大缺陷就是:line-box的基线的位置受其内部所有元素的影响。我们假设这样一种情况,一个元素相对于line-box的基线对齐,那么当line-box的基线位置发生改变时,那么元素的位置也会跟着改变。

来看一些例子:

  • 如果有一个比较高的元素刚好撑满了line-box,vertical-align对它来说就没用任何影响了。因为它的下面和上面已经没有位置可以让它再移动了。下面两幅图中矮小的盒子都设置vertical-align:baseline。左图的高盒子设置为vertical-align:text-bottom。右图的高盒子设置为vertical-align:text-top。你就会发现两幅图中line-box的基线是不再同一位置的,因为矮盒子是跟着基线对齐的。
    Paste_Image.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的值为bottom(左图)和top(右图)也会移动基线的位置。
    Paste_Image.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的高度和基线都会重新调整。这种情况我们的前两个盒子就会下移(如右图)。


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

inline级的元素下面为什么会有一个小空隙

看看下面的例子。通常都会有如下情况出现,当你想对齐竖直对齐li元素的时候。


Paste_Image.png
<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元素其实默认是和基线对齐的,基线下面是留有一部分空白的,这个空白是可以容纳半个“x”的空间。这就导致了空隙的存在。如何解决?我们可以改变基线的位置,例如给li元素设置对齐方式为vertical-align: middle

Paste_Image.png
<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-block元素身上,因为文本内容已经把基线的位置抬高了

inline级元素之间的缝隙会打断布局

这个问题主要还是inline级元素自身问题造成的。但是鉴于这种情况也影响了竖直对齐,我们还是了解一下比较好。

和上个例子一样,这个例子也是由空隙造成的。这个空隙主要来自于inline元素之间的空格符。inline元素之间的所有空格符会合并为一个空格。如果你想让两个inline元素并排显示,并且width:50%,那么这个空格就是个绊脚石。一行的空间不足以容纳两个width:50%和一个空格。所以右边的元素就被挤下去了。要想消除缝隙,你就得消除空格。

Paste_Image.png

<!-- 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再不听话了,就问如下的两个问题:

  • line-box的基线,上下边沿在哪里?
  • inline级元素的基线,上下边沿在哪里?

我相信问题马上就会得到解答。


至此结束,不知道有几个人能耐心的看完……

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容