有些東西我們經(jīng)常用,但是我們卻并不了解它的原理,所以一旦換了場景,好多東西就不知道該怎么用了。最近一直很糾結(jié)vertical-align
到底是怎么回事,因?yàn)椴还苁谴怪睂R元素,還是消除inline-block的縫隙也好,我們總能用到它,可是就是不知道為什么。抱著刨根問底的態(tài)度,今天在網(wǎng)上發(fā)現(xiàn)了一篇好文章,無奈是英文,博主不是什么英語大神,看到英語也頭疼,但奈何好文章都是英文。那就來吧,翻譯一下午,雖然是撕心裂肺,但是感覺受益匪淺,文中有翻譯不當(dāng)?shù)牡胤竭€請各位看官多多指正。
小二,上原文鏈接:Vertical-Align: All You Need To Know
我們經(jīng)常需要在豎直方向上對齊一些并肩排列的元素。
CSS為我們提供了一些實(shí)現(xiàn)方法。有時(shí)候我會(huì)使用float
,有時(shí)候也用position:absolute
,有時(shí)候甚至利用手動(dòng)添加內(nèi)外邊距的方法。
我并不喜歡這些方法。浮動(dòng)只能對齊它們的頂部,而且需要手動(dòng)清除浮動(dòng)。絕對定位會(huì)使元素脫離文檔流,以致于它們就不能影響周圍的元素了。再說說使用固定的內(nèi)外邊距,會(huì)突然導(dǎo)致微弱的改變。
這里還有另一種值得信任的方法,那就是vertical-align
。從專業(yè)角度講,使用vertical-align
來進(jìn)行布局是一種“黑科技”,因?yàn)榘l(fā)明它并不是出于這個(gè)目的。vertical-align
是為了對齊文本和緊鄰文本的元素。盡管如此,你也能在不同場景下使用vertical-align
去進(jìn)行靈活細(xì)微的元素對齊工作,而且你并不需要知道元素的具體尺寸。
Vertial-Align的“詭異”
但是,vertical-align
有時(shí)候真的讓人抓狂。它似乎有一些不可告人的秘密。例如,有時(shí)候你對一些元素設(shè)置vertical-align
,你會(huì)發(fā)現(xiàn)它的對齊方式并沒有改變,但是同一行的其它元素卻變了。我時(shí)常哭暈在vertical-align
的廁所中。
不幸的是,大部分關(guān)于它的資料都是淺顯的。尤其是,如果你想找vertical-align
關(guān)于布局方面的介紹時(shí),他們大多告訴你錯(cuò)誤的信息——vertical-align
可以控制元素內(nèi)部所有元素的豎直排列。他們總是給出最基礎(chǔ)的場景應(yīng)用,卻從不介紹復(fù)雜的情況。
所以我給自己立下任務(wù),要一次弄清所有關(guān)于vertical-align的知識。我看完W3C的CSS說明然后自己動(dòng)手做了一些實(shí)驗(yàn)。結(jié)果就是這篇文章了。
那么,然我們一起來了解游戲規(guī)則吧。
使用vertical-align的前提條件
vertical-align
用于對齊行內(nèi)級元素。行內(nèi)級元素的display
屬性是如下值中的一種:
- inline
- inline-block
- inline-table(本文不涉及,有興趣自己看)
inline元素是包裹文本的基本標(biāo)簽。
inline-block元素顧名思義是存在于inline中的block元素。它們可以設(shè)置寬高、內(nèi)外邊距、和邊框。
inline級元素是一個(gè)挨著一個(gè)排在一行的元素。一旦一行放不下所有元素的時(shí)候,就會(huì)在下面創(chuàng)建一個(gè)新行。所有的行都有一個(gè)叫做line-box的東西,它是用來包裹這一行所有內(nèi)容用的。line-box的高度是根據(jù)一行內(nèi)所有內(nèi)容的高度定的。如下圖中所示:line-box的上下邊沿用紅線標(biāo)出。(line-box的邊沿平時(shí)是看不到的,畫出來是為了方便讀者理解)
line-box規(guī)定出了我們對齊元素時(shí)的活動(dòng)范圍。在line-box中
vertical-align
負(fù)責(zé)每個(gè)元素的豎直對齊方式。那么問題就來了,什么是元素的對齊呢?
關(guān)于base-line(基線)和outer edge(外邊沿)
理解垂直對齊最關(guān)鍵的一點(diǎn)在于理解相關(guān)元素的基線。還有一些場景中,元素的上下邊沿也至關(guān)重要。現(xiàn)在就讓我們看一下不同元素的基線和上下邊沿到底在哪里?
inline元素
從上圖中你可你可以看到三種情況。行高的上下邊沿用紅線表示,綠線表示字體的高度,藍(lán)線表示基線。左圖中的行高和字體尺寸相同,所以紅線和綠線重合在一起了。中間的圖片,行高是字體高度的兩倍。右圖行高是字體高度的一半。
行內(nèi)元素的外邊沿就是它們的行高的上下邊沿。即使行高比字體高度小也沒有關(guān)系。因此,行內(nèi)元素的外邊沿就是上圖中的紅線。
行內(nèi)元素的基線是字符下面的一條線。具體是圖中的藍(lán)線。大約的說,基線是字體正中線下面的一條線。想具體了解的請看W3C的詳細(xì)說明
inline-block元素
從左到右依次是:一個(gè)包含in-flow內(nèi)容(一個(gè)字母C)的inline-block元素;一個(gè)包含in-flow內(nèi)容并且設(shè)置了
over-flow:hidden
屬性的inline-block元素;一個(gè)沒有in-flow內(nèi)容(但是設(shè)置了高度屬性)的inline-block元素。三個(gè)例子都設(shè)置了margin屬性,紅線是margin的邊緣,黃色部分是邊框,綠色部分是內(nèi)邊距padding,藍(lán)色部分是內(nèi)容。藍(lán)線是各個(gè)元素的基線。inline-block元素的外邊沿是它們的margin-box的上下邊沿。也就是圖中的紅線。
inline-block元素的基線取決于元素是否有in-flow內(nèi)容:
- 包含in-flow內(nèi)容的inline-block元素的基線是普通文檔流中最后一個(gè)元素內(nèi)容的基線(例如左圖)。這最后一個(gè)元素的基線如何定位,那就看它是什么類型的元素了。
- 包含in-flow內(nèi)容而且設(shè)置了overflow屬性(值非visible)的inline-block元素,其基線是margin-box的下邊沿(例如中間的圖)。所以它的基線就和自己的底邊沿重合了。
- 不包含in-flow內(nèi)容的inline-block元素和上者相同,同樣是底邊線。
什么是line box?

我為line-box中的text-box(下面會(huì)講到)加上了上下邊沿(綠線)以及基線(藍(lán)線)。我也為文字元素加上了背景色(灰色)。
line-box有一個(gè)上邊沿,它和行內(nèi)最高的元素的上邊沿對齊;同樣也有一個(gè)下邊沿和行內(nèi)最低元素的下邊沿對齊。這兩個(gè)邊沿也就是圖中的紅線。
line-box的基線是會(huì)變化的:
"CSS 2.1 does not define the position of the line box's baseline."
——the W3C Specs
這可能是最令人困惑的部分。這就意味著,line-box的基線是會(huì)根據(jù)一定的規(guī)則動(dòng)態(tài)改變的。
由于line-box的基線是不可見的,所以你也不可能準(zhǔn)確的說出它到底在哪里。但是能通過一個(gè)簡單方法知道它在哪。只要在一行內(nèi)容的行首添加一個(gè)字符就行,例如:我加了一個(gè)"x"(行首灰色的那個(gè))。如果這個(gè)字符沒有進(jìn)行任何人為方式的對齊,那么它的基線就默認(rèn)是在line-box的基線上。
line-box的基線周圍的空間,我們可以稱之為text box。你可以把text box看做是一個(gè)在line-box中沒有進(jìn)行任何對齊的inline元素。它的高度等于父元素的字體大小。因此text box是用于包裹line box中沒有設(shè)置任何對齊的文字的。圖中綠線之間的部分就是text box。由于text box是和基線緊密相連的,因此它會(huì)隨著基線的移動(dòng)而移動(dòng)。(在W3C說明中,text box叫做strut)
現(xiàn)在我們知道了所有的預(yù)備知識點(diǎn),讓我們做一下總結(jié):
- 有一個(gè)叫做line-box的區(qū)域。各個(gè)元素是要在這里進(jìn)行垂直對齊的。它包含一條基線,一個(gè)text box,一個(gè)上邊沿,一個(gè)下邊沿。
- 這里還有一些inline級別的元素。這些都是我們要進(jìn)行對齊的對象。它們同樣有自己的基線,一個(gè)上邊沿,一個(gè)下邊沿。
Vertical Align都能使用哪些值
我們通過使用vertical-align
可以把上面我們介紹的那些參考點(diǎn)(基線,上線邊沿等)設(shè)置成確定的關(guān)系。
主要分一下兩種情況:
將元素的基線相對于line-box的基線進(jìn)行對齊

- baseline:將元素的基線對齊到line-box的基線上;
- sub:將元素的基線對齊到line-box基線下方;
- super :將元素的基線對齊到line-box的基線上方;
- <percentage> :元素的基線相對于line-box的基線進(jìn)行確定距離的對齊,定位的距離和方向由設(shè)置的百分比數(shù)值決定,百分比是相對于line-height的。
- <length>:元素的基線相對于line-box的基線進(jìn)行確定距離的對齊,定位的距離和方向由設(shè)置的具體長度值決定。
將元素的外邊沿相對于line-box的基線進(jìn)行對齊
- middle:使元素的上沿和下沿的中點(diǎn)對齊到字母“x”的基線上邊加上一半x的高度上(其實(shí)就是字母x的正中間)。
將元素的外邊沿相對于line-box的text box對齊
以下的兩種情況,其實(shí)也是相對于line-box的基線的一種對齊,因?yàn)閠ext-box的上下邊沿也是取決于基線的。
- text-top:元素的上邊沿對齊到line-box的text-box的上邊沿;
- text-bottom:元素的下邊沿對齊到line-box的text-box的下邊沿。
將元素的外邊沿相對于line-box的外邊沿對齊
- top:將元素的上邊沿和line-box的上邊沿對齊;
- bottom:將元素的下邊沿和line-box的下邊沿對齊;
為什么Vertical-Align會(huì)表現(xiàn)出這樣的作用呢?
我們現(xiàn)在能夠在具體的場景下更細(xì)致的研究vertical-align了。尤其是,我們將解決一些常見的問題。
使圖標(biāo)和文字居中
長久以來一直困擾我的一個(gè)問題:我想讓一個(gè)圖標(biāo)和它旁邊的文字居中對齊時(shí),僅僅對圖標(biāo)使用vertical-align:middle
似乎并沒有真正的使兩者居中對齊。
來看看下面這個(gè)例子:
<!-- 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>
下圖還是這個(gè)例子,不過我為其添加了一些標(biāo)注幫助理解,這些線都是我們之前已經(jīng)講過的:
這樣看是不是就有些明白是怎么回事了。因?yàn)樽髨D中的文字沒有經(jīng)過對齊處理多以它默認(rèn)是在text-box中的,它的基線和line-box的基線是重合的。通過給灰色方塊設(shè)置
vertical-align: middle;
,我們的確把灰色方塊的正中心對準(zhǔn)了x字母的正中心,但是x字母的正中心并不是其所在的text-box的正中心,而是text-box中心稍稍的偏下了一點(diǎn)。再看右圖,我們把“center”包裹起來并為也設(shè)置
vertical-align: middle;
,“center”的基線就不再和line-box的基線重合了,而是稍稍下移了。結(jié)果圖標(biāo)和右邊的文字就完美的居中對齊了。
line-box的基線是會(huì)移動(dòng)的
使用vertial-align是的一大缺陷就是:line-box的基線的位置受其內(nèi)部所有元素的影響。我們假設(shè)這樣一種情況,一個(gè)元素相對于line-box的基線對齊,那么當(dāng)line-box的基線位置發(fā)生改變時(shí),那么元素的位置也會(huì)跟著改變。
來看一些例子:
- 如果有一個(gè)比較高的元素剛好撐滿了line-box,
vertical-align
對它來說就沒用任何影響了。因?yàn)樗南旅婧蜕厦嬉呀?jīng)沒有位置可以讓它再移動(dòng)了。下面兩幅圖中矮小的盒子都設(shè)置vertical-align:baseline
。左圖的高盒子設(shè)置為vertical-align:text-bottom
。右圖的高盒子設(shè)置為vertical-align:text-top
。你就會(huì)發(fā)現(xiàn)兩幅圖中l(wèi)ine-box的基線是不再同一位置的,因?yàn)榘凶邮歉€對齊的。
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>
當(dāng)我們?yōu)檫@個(gè)比較高的元素設(shè)置其它的vertical-align
值時(shí),也會(huì)有相同的表現(xiàn)。
- 即使設(shè)置
vertical-align
的值為bottom
(左圖)和top
(右圖)也會(huì)移動(dòng)基線的位置。
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>
-
將兩個(gè)比較高的盒子放置在一行,調(diào)整它們兩個(gè)的垂直對齊方式使基線能夠同時(shí)滿足兩種對齊。然后line-box的的高度就得到了調(diào)整(如左圖)。再添加第三個(gè)元素,它的高度不會(huì)超過line-box的邊緣,因?yàn)樗亩ㄎ环绞郊炔粫?huì)影響line-box的高度,也不會(huì)影響基線的位置(如中圖)。如果它真的超出了原有l(wèi)ine-box的邊緣,line-box的高度和基線都會(huì)重新調(diào)整。這種情況我們的前兩個(gè)盒子就會(huì)下移(如右圖)。
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級的元素下面為什么會(huì)有一個(gè)小空隙
看看下面的例子。通常都會(huì)有如下情況出現(xiàn),當(dāng)你想對齊豎直對齊li元素的時(shí)候。
<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元素其實(shí)默認(rèn)是和基線對齊的,基線下面是留有一部分空白的,這個(gè)空白是可以容納半個(gè)“x”的空間。這就導(dǎo)致了空隙的存在。如何解決?我們可以改變基線的位置,例如給li元素設(shè)置對齊方式為vertical-align: middle
<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>
這種情況不會(huì)發(fā)生在包含文本內(nèi)容的inline-block元素身上,因?yàn)?strong>文本內(nèi)容已經(jīng)把基線的位置抬高了。
inline級元素之間的縫隙會(huì)打斷布局
這個(gè)問題主要還是inline級元素自身問題造成的。但是鑒于這種情況也影響了豎直對齊,我們還是了解一下比較好。
和上個(gè)例子一樣,這個(gè)例子也是由空隙造成的。這個(gè)空隙主要來自于inline元素之間的空格符。inline元素之間的所有空格符會(huì)合并為一個(gè)空格。如果你想讓兩個(gè)inline元素并排顯示,并且width:50%
,那么這個(gè)空格就是個(gè)絆腳石。一行的空間不足以容納兩個(gè)width:50%
和一個(gè)空格。所以右邊的元素就被擠下去了。要想消除縫隙,你就得消除空格。
<!-- 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解密
就是這樣。一旦你知道了規(guī)則之后它看起來就不那么復(fù)雜了。如果下次vertical-align
再不聽話了,就問如下的兩個(gè)問題:
- line-box的基線,上下邊沿在哪里?
- inline級元素的基線,上下邊沿在哪里?
我相信問題馬上就會(huì)得到解答。
至此結(jié)束,不知道有幾個(gè)人能耐心的看完……