原文地址:Vertical-Align: All You Need To Know
通常我們需要垂直對齊並排的元素。
CSS提供了一些可實現的方法:有時我用浮動float來解決,有時用position: absolute來解決,有時甚至是“骯臟”地手動添加的margin或padding。
我真的不喜歡這些解決方案。float只對齊元素的頂部,且需要手動清除浮動。絕對定位的元素脫離文檔流,所以他們不再影響周圍的元素。而用修補性的margin或padding打破了最細微變化的東西。
但這里還有一個選擇:vertical-align。我認為它值得我們更多地信任和使用。在技術上,使用vertical-align
的布局是一種hack,因為vertical-align
不是為這個目的(垂直對齊布局)發明的。它的存在,是用以對齊並排在一起的文本和元素。盡管如此,你還是可以在不同的上下文中,使用vertical-align
靈活而精確地對齊相鄰元素。元素的大小不必是已知的。元素留在文檔流中,所以周圍的其他元素可以會根據這個元素受到的改變而做出反應。這使得vertical-align
成為一個有價值的選擇。
vertical-align
的特殊性
但是,vertical-align
有時候很操蛋,它會令你抓狂,似乎它在使用中有一些神秘的規則。例如,你可能改變了元素的vertical-align
,但沒有出現你想要的效果,但行中的其他元素卻被改變了!這時我就很想跑到黑暗的角落里撓牆,撕扯我的頭發。
不幸的是,大部分關於vertical-align
的文章資源都有點淺,特別是關於使用vertical-align
來布局的。他們經常誤解vertical-align
,試圖用它垂直對齊一個容器內的所有元素。他們給vertical-align
做基本的介紹,並解釋如何在非常簡單的情況下對齊元素,卻不解釋棘手的部分。
所以,我給自己設定了目標,希望一次說清垂直對齊的所有行為。我最終通過W3C的CSS規范和一些實例總結得出本文。
所以,讓我們來理清vertical-align
的
游戲規則。
使用vertical-align
的條件
vertical-align
是用來對齊內聯級元素的。
設置為以下display屬性的元素,它們都被認為屬於內聯級元素。
inline,
inline-block or
inline-table (本文中不涉及此種情況)
inline內聯元素基本上是包裹文本的標簽。
inline-block內聯塊元素則如它們的名字所示:擁有內聯特性的塊元素。他們可以有width和height(可能是由自己的內容定義),以及padding、border和margin。
內聯級元素彼此緊挨着放在一行中。一旦有更多的元素被放置到當前行中,一個新的行將會在它下面創建。所有這些行有所謂的“行框”,行框中包含所有的內容。不同大小的內容意味着不同高度的行框。在下面的插圖中,行框的頂部和底部都是用紅線表示的。
行框(line box)勾勒出我們正在設計的區域(下文會詳細介紹“行框”的概念)。在行框中,元素的vertical-align
屬性是負責垂直對齊的。那么,到底元素垂直對齊的參照物是什么?
參照物:父元素的基線和外邊緣
vertical-align與元素
的基線息息相關。在某些情況下,元素的行框的頂部和底部邊緣也變得很重要。讓我們看看元素的基線和行框的外觀:
·內聯元素
此處,你可以看到三行並排的文本。行框的頂部和底部邊緣用紅線表示,字體的高度由綠線表示,基線由一條藍線表示。在左邊,有一個line-height設置為與字體font-size大小相同高度的文本,綠線和紅線重疊在一條線上。在中間,line-height是字體的兩倍大。在右邊,line-height是字體大小的一半大。
內聯元素(display:inline)的外邊緣與其行高的頂部和底部邊緣對齊,行高可以小於字體的高度。所以,行框就是上面的圖中的紅線。
內聯元素的基線是字符放置的位置線(字母x底部所在的水平線),即圖中的藍線。粗略地說,基線是在字體1/2高度的下面的某個地方。感興趣的可以看看W3C規范的詳細定義。
·內聯塊元素
從左到右,你可以看到:在左邊,是一個內聯塊元素包含了一個流內內容(一個“C”);在中間,一個內聯塊元素包含了一個流內內容(也是一個“c”),元素屬性有overflow: hidden;在右邊,一個內聯塊元素包含了一個流外內容(但內容區域有一個高度)。(譯者注:流內的元素必須是普通文檔流(normal flow)中的元素,流外的元素必須是浮動或絕對定位的元素以及根元素。)margin的邊界是由紅線表示,border是橘黃色的,padding是綠色的和內容區域是藍色的。每個內聯塊元素的基線為一條藍色線。
內聯塊元素的外邊緣是其margin框的頂部和底部邊緣,即圖中的紅線。
內聯塊元素的基線取決它包含的內容是否在文檔流中:
- 在流內內容的情況下,內聯塊元素的基線是正常流中最后一個內容元素的基線(左邊的例子)。對於這最后一個元素,它的基線是根據它自己的規則找到的。
- 在流內內容但內聯塊元素有overflow:hidden屬性的情況下,基線是內聯塊元素margin框的底部邊緣(例如在中間)。
- 在流外內容的情況下,基線是內聯塊元素margin框的底部邊緣(例如在右邊)。
·行框
你已經看到了上面的設定。這一次,我將在行框里的文本框頂部和底部畫上綠線,以及基線(藍色)。我還用灰色背景強調了該區域的文本元素。
該行框的頂部邊緣對齊該行中最頂部元素的頂部邊緣,底部邊緣對齊該行中的最底部元素的底部邊緣。如上圖中紅線所表示的盒子。
·行框的基線是可變的:
當使用vertical-align
時,基線放置在哪里可能是最令人疑惑的部分。它需要滿足vertical-align
的值和行框的高度等所有條件。基線的位置猶如是方程中的一個自由參數。
行框的基線是看不見的,但你可以使它很容易看到。只要在文本行的開頭添加一個字符,像我增加了一個“X”的字母。如果這個字符不以任何方式對齊,它將默認地坐在基線上。
圍繞着行框的基線的部分(綠線),我們可以稱其為文本框。文本框可以簡單地被認為是行框內的內聯元素,沒有任何對齊。文本框的高度等於它的父元素的字體大小。因此,文本框只圍住了行框內的無格式文本。由於這個文本框是綁在基線上的,當基線移動時它將移動。(注:此文本框在W3C規范中稱為“strut(支柱)”)
好了,以上是最難的部分。現在,讓我們迅速總結一下上面的重點:
- 有一個稱為行框的區域。這是垂直對齊發生的區域。它有一個基線,一個文本框和一個頂部和底部邊緣。
- 有一些稱為內聯級元素的元素。它們都是會對齊的對象。他們有一個基線和一個頂部和底部邊緣。
vertical-align
的值
它的值分為以下幾種對齊方式:
1)將元素的基線,參照父元素的基線對齊
baseline:元素的基線與父元素的基線對齊。
sub:元素的基線偏移到父元素的基線之下。
sup:元素的基線偏移到父元素的基線之上。
<percentage>:元素的基線相對於父元素的基線偏移了一個百分比(該百分比是對比元素自身的line-height計算得出)。
<length>:元素的基線相對於父元素的基線偏移了一個絕對長度。
2)將元素的中心點,參照父元素的基線對齊
middle:將元素的頂部和底部之間的中心點,對齊父元素的基線之上x-height的1/2之處(x-height為字母x的字符高度)。
3)將元素的外邊緣,參照父元素的文本框對齊
text-top:將元素的頂部邊緣,對齊到父元素的文本框的頂部邊緣。
text-bottom:將元素的底部邊緣,對齊到父元素的文本框的底部邊緣。
4)將元素的外邊緣,參照父元素行框的外邊緣對齊
top:元素的頂部邊緣對齊到父元素的頂部邊緣。
bottom:元素的底部邊緣對齊到父元素的底部邊緣。
當然更正式的定義可以參考W3C規范
為什么vertical-align
會這樣呈現?
我們現在可以仔細看看以下幾種使用vertical-align有問題的情況:
1)垂直居中一個圖標
我們經常遇到這樣的需求,我有一個圖標,我想將它垂直居中於一行文本。只給圖標設置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>
我們畫了輔助線方便理解(上文中我們知道每種顏色的線代表什么)
這樣我們的答案更清晰了:在左邊,文本並沒有設置居中對齊,它位於基線上。原因是我們把圖標設置了vertical-align: middle,但文本並沒有設置垂直居中。所以,文本站在了基線上面,而圖標的中點對齊到父元素的基線之上1/2 x-height的位置。
在右邊,我們讓圖標和文字都設置了vertical-align: middle。文本的基線和圖標的中點都對齊到父元素的基線之上1/2 x-height的位置。
2)行框基線的移動
這是一個常見的陷阱,當使用vertical-align:行框的基線位置會被其內部的所有元素影響。如果一個容器內的元素是按以下其中一種方式排列的,那么它的行框的基線必須移動。
一些例子:
如果一行中有一個高個的元素占據了整行的高度,那么vertical-align對它沒有影響。它的頂部和底部沒有空間讓它移動。為了滿足行框基線的對齊方式,行框的基線必須移動。矮個元素設置了vertical-align: baseline。在左邊,高個元素設置了vertical-align: text-bottom。在右邊,高個元素設置了vertical-align: 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>
當高個元素設置為其他的值時,也有同樣的效果出現。
即使設置vertical-align
為 bottom
(左) 和 top
(右),基線也會移動。
<!-- 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
,它們會使得行框的基線移動到符合它倆的對齊規則之處,然后行框的高度也會隨之調整。(中)添加第三個元素,不超越行框的邊緣,既不影響行框的高度,也不影響基線的位置。(右)添加第三個元素,如果它超出了行框的邊緣,行框的高度和基線調整。在這種情況下,我們的前兩個元素也會跟着發生變化。
<!-- 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>
3)內聯級元素底部的小間隙
看一看以下情況。這很常見,如果你嘗試垂直對齊列表中的元素。
<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>
正如你所看到的,列表項坐在基線上。下面的一點空間,是文本的基線以下預留的depth(在W3C規范中,一個字體的基線以上稱為characteristic height,基線以下稱為depth)。想要去掉這個depth空隙,有解決的辦法嗎?只要移動基線的位置就可以,例如通過設置列表項目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>
這種情況不會發生在含有文本內容的內聯塊元素中,因為內容已經將基線移動了。
4)內聯級元素之間的空隙打破了布局
這主要是內聯級元素本身的問題。但因為他們常使用垂直對齊,我們應該了解清楚這個問題。
在上面的示例中,我們可以看列表項之間的間隙。該間隙來自於在你的內聯元素標簽之間的空格white-space。在內聯元素之間的所有空格都被折疊成一個空間。例如,如果我們想並排放置兩個內聯元素,並給他們width:50%,這時就沒有足夠的空間放置兩個寬50%元的素和一個間隙。因此,它們會被分成兩行破壞了布局(左)。為了消除間隙,我們需要想辦法消除空格white-space,例如用HTML注釋(右)。
<!-- 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的揭秘。一旦你知道了規則,它就不會很復雜了。如果垂直對齊沒有表現出你要的效果,只需要問以下問題:
行框的基線以及頂部和底部邊緣在哪里?
內聯級元素的基線和頂部和底部邊緣在哪里?
這將是解決問題的方案。