寫在前面
本文第一部分翻譯自 Vertical-Align: All You Need To Know,就是之前在 [CSS 上下左右居中](/articles/css 上下左右居中/) 參考資料部分提到的待翻譯的那一篇
其餘部分是對原文的技巧總結
.top{display:inline-block;vertical-align:top}.bottom{display:inline-block;vertical-align:bottom}.middle{display:inline-block;vertical-align:middle}.baseline{display:inline-block;vertical-align:baseline}.text-top{display:inline-block;vertical-align:text-top}.text-bottom{display:inline-block;vertical-align:text-bottom}.sub{display:inline-block;vertical-align:sub}.super{display:inline-block;vertical-align:super}strong{font-weight:400}figure{line-height:1;overflow:visible}figure ul,figure ol{margin:0 !important;;padding:0 !important;}figure.center{text-align:center}figure.bg-grey{background:#f0f0f0;padding:1em}figure .center{text-align:center;display:inline-block;width:100%}figure+pre{margin-top:1em}figure *+*{margin-top:0}figure figcaption{width:100%;margin:0;margin-top:1em;font-style:italic;font-size:.8em}figure .bg-grey{background:#d3d3d3}figure .bg-green{background:#c1cd89}figure .bg-yellow{background:#fcdb9a}figure .bg-blue{background:#8ab3bf}figure .border-grey{border:1px solid #d3d3d3}figure .font{white-space:nowrap;line-height:1}figure .font.small{font-size:.6666em}figure .font.smaller{font-size:.3333em}figure .font.large{font-size:1.5em}figure .font.larger{font-size:3em}figure .font.tall-line-height{line-height:2}figure .font.short-line-height{line-height:.5}figure .font.color-grey{color:#d3d3d3}figure .box{min-width:1em;min-height:1em}figure .box.shorter{min-height:.25em}figure .box.shorter.quad{min-width:.25em}figure .box.short{min-height:.5em}figure .box.short.quad{min-width:.5em}figure .box.tall{height:2em}figure .box.tall.quad{width:2em}figure .box.taller{height:4em}figure .box.taller.quad{width:4em}figure .box.quad.max-third-width{max-width:30%}figure .inline-overlay{display:inline-block;width:100%;margin-right:-100%;position:relative;z-index:10}figure .line{display:inline-block;width:100%;margin-right:-100%;position:relative;z-index:10;border-top:1px solid #000}figure .line.dashed{border-style:dashed}figure .line.dotted{border-style:dotted}figure .line.grey{border-color:#d3d3d3}figure .line.red{border-color:red}figure .line.blue{border-color:#00f}figure .line.green{border-color:#32cd32}figure .line.orange{border-color:#daa520}figure .show-box-model{background:#c1cd89;border-color:#fcdb9a}figure .show-box-model>.show-box-model-content{background:#8ab3bf;display:block;min-width:100%;min-height:100%} .columns.no-break>* { margin-right: 2.5%; width: 47.5% !important; display: inline-block; margin-top: 0; } .columns.no-break.thirds>* { margin-right: 2.5%; width: 30% !important; display: inline-block; margin-left: 0; }經常需要讓一些並排顯示的元素豎直對齊
CSS 提供了一些可選方案,有時通過 float 來解決,有時用 position: absolute,有時甚至用手動添加 margin 或 padding 這樣的髒方法,我不很喜歡這些方案。浮動只是讓它們頂部對齊,而且要手動清除(浮動的影響)。絕對定位讓一些元素脫離標準文檔流,以至於它們無法再影響周圍元素。而即使是最微小的變動也會破壞固定 margin 和 padding
但還有另外一個角色:vertical-align。我覺得它更值得信任。雖然在技術上,用 vertical-align 實現佈局是一種 hack,因為它不是為佈局設計的,而是用來對齊文本與文本旁邊元素的。但是,也能用 vertical-align 在不同環境中靈活且細粒度(fine-grained)地對齊元素。不需要知道元素的大小,元素仍然處於標準文檔流中,其它元素能響應其尺寸變化。這些優勢讓它成了一個有價值的選項
vertical-align 的怪脾氣
但 vertical-align 有時候真的很討厭,用起來會有些挫敗感,似乎有一些神秘的規則。例如,可能會遇到,改變元素的 vertical-align 根本沒有改變它自己的對齊方式,但同一行的其它元素(的對齊方式)卻變了!現在還時不時地鑽進這些陰暗的角落,讓我抓狂(tearing my hair)
不幸的是,大多數相關資源都太淺顯了,尤其是在我們想用 vertical-align 實現佈局時。他們專注於試圖讓一個元素裡面的所有東西都豎直對齊的錯誤想法,給出屬性的基本介紹,並解釋非常簡單的場景下元素的對齊方式,而不解釋技巧性的部分
所以,我給自己定下了一勞永逸地澄清 vertical-align 行為的目標,以深入 W3C 的 CSS 規範,並嘗試一些例子告終,最終成果就是本文
那麼,下面我們從遊戲規則入手
vertical-align 的依賴項
vertical-align 用來對齊內聯級(inline-level)元素,也就是那些 display 屬性的計算值為:
-
inline -
inline-block -
inline-table(本文不考慮)
內聯元素(inline elements)是基本標籤包裹著的文本
內聯 - 塊元素(inline-block elements)就像它名字所說的那樣:內嵌的塊元素(block elements living inline)。它們可以具有 width,height(也有可能是通過其內容確定的)和 padding,border 及 margin
內聯級元素(inline-level elements)在一行中一個挨一個地排列,一旦當前行放不下了,就在它下方創建一個新行,所有這些行都具有所謂的行盒(line box),包住這一行的所有內容。不同大小的的內容意味著不等高的行盒。下圖中行盒的上下邊界用紅線標出來了:
A tall in a line of text.A short in a line of text.
This can happen.
行盒就是我們的上下文(the line boxes trace out the field we are playing on),這些行盒中的 vertical-align 屬性負責對齊各個元素。那麼,元素對齊到底是怎麼回事?
baseline 和 outer edge
豎直對齊最重要的參照點是相關元素的 baseline,某些情況下,元素包裹盒的頂邊和底邊也很重要。我們一起看看各種類型元素的 baseline 和 outer edge 在哪裡:
內聯元素
-->
-->
可以看到 3 行並列的文本,行高的頂邊和底邊用紅線表示出來,字體的高度用綠線,baseline 用藍線。左邊文本的行高設置為與 font-size 相同,綠線和紅線重合了。中間文本行高是 font-size 的 2 倍。右邊行高是 font-size 的一半
內聯元素的 outer edge 與其行高的頂邊和底邊對齊,如果行高小於字體高度的話,就無所謂。所以,outer edge 是上圖中的紅線
內聯元素的 baseline 是字符坐在上面的那條線(baseline is the line, the characters are sitting on),即圖中的藍線。很難理解的是,baseline 有時會在字體高度的下方,見 W3C 規範的 詳細定義
內聯 - 塊元素
-->
-->
從左到右依次是:含有流內(in-flow)內容(那個"c")的內聯 - 塊元素,含有流內內容和 overflow: hidden 的內聯 - 塊元素和不含流內內容(但內容區具有高度)的內聯 - 塊元素。margin 的邊界用紅線表示出來,border 為黃色,padding 為綠色,內容區為藍色,每個內聯 - 塊元素的 baseline 用藍線表示
內聯 - 塊元素的 outer edge 是其 margin-box 的頂邊和底邊,也就是圖中的紅線
內聯 - 塊元素的 baseline 取決於元素是否含有流內內容:
-
含有流內內容時,內聯 - 塊元素的 baseline 是常規流中最後一個內容元素的 baseline(左邊的例子),最後一個元素的 baseline 是根據它自身的規則來確定的
-
含有流內內容但具有計算值為非
visible的overflow屬性時,baseline 是 margin-box 的底邊(中間的例子),所以,它與內聯 - 塊元素的底邊相同 -
不含流內內容時,baseline 也是 margin-box 的底邊(右邊的例子)
行盒
x This can happen.上圖中,把行盒的文本盒(更多信息見下文)的頂邊和底邊用綠色畫出來,而 baseline 還用藍線,還給文本元素設置了灰色背景高亮標記出來
行盒的頂邊與該行最高元素的頂邊對齊,並且底邊與該行最低元素的底邊對齊,就是上圖中用紅線表示的部分
行盒的 baseline 是可變的:
CSS 2.1 does not define the position of the line box's baseline. — the W3C Specs
這可能是用 vertical-align 時最讓人迷惑的部分了。也就是說,baseline 具體放在哪裡要滿足所有其它條件,比如 vertical-align 和讓行盒高度最小,它是方程中的一個自由參數
因為行盒的 baseline 是不可見的,無法直觀地看出來它在哪裡。但很容易就能讓它變得可見,只需要在有疑問的行首添一個字符,就像圖中添的"x"。如果這個字符沒有以任何方式對齊,它默認將坐在 baseline 上
在 baseline 周圍,行盒含有我們稱之為文本盒(text box)的東西。文本盒可以簡單地看做一個沒有任何對齊方式的行盒中的內聯元素。其高度等於其父元素的 font-size。因此,文本盒只會包裹行盒中沒被格式化過的文本,上圖中用綠線表示出來了。因為這個文本盒與 baseline 綁在一起,baseline 動的時候它也跟著動(注:這個文本盒在 W3C 規範中被稱為 strut)
這是最難的部分了。現在,我們已經知根知底了。快速總結一下最重要的幾點:
-
有個區域叫行盒,是豎直對齊發生的地方。它具有 baseline,文本盒及頂邊底邊
-
內聯級元素,是哪些被對齊的東西,它們具有 baseline 和頂邊底邊
vertical-align 的值
通過使用 vertical-align 來對上面提到的參照點和內聯級元素設定某些關聯
元素的 baseline 相對行盒 baseline 對齊
x baseline sub super -50% +10px-
baseline:元素的 baseline 恰好與行盒的 baseline 重合 -
sub:元素的 baseline 移到行盒 baseline 下方 -
super:元素的 baseline 移到行盒的 baseline 上方 -
<percentage>:元素的 baseline 相對行盒的 baseline 移動關於line-height的百分比 -
<length>: 元素的 baseline 相對行盒的 baseline 移動一個絕對長度
元素的 outer edge 相對行盒 baseline 對齊
x middlemiddle:元素頂邊底邊之間的中點與行盒的 baseline 加上半個 x-height 對齊
元素的 outer edge 相對行盒的文本盒對齊
x text-top text-bottom-
text-top:元素的頂邊與行盒的文本盒的頂邊對齊 -
text-bottom:元素的底邊與行盒的文本盒的底邊對齊
元素的 outer edge 相對行盒的 outer edge 對齊
x top bottom-
top:元素的頂邊與行盒的頂邊對齊 -
bottom:元素的底邊與行盒的底邊對齊
當然,正式的定義 在 W3C 規範裡都能找到
為什麼 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>
這兒還有個相同的例子,但我畫出了一些你已經從上面了解到的輔助線:
這樣能揭示一些線索,因為左邊的文本沒有任何對齊方式,它坐在 baseline 上。實際上,設置 vertical-align: middle 來對齊小方塊,我們把它對齊到了不具上伸部(ascender)的小寫字母的中心位置(半個 x-height)。所以,具有上伸部的字符顯得比較靠上
右邊的話,我們讓整個字體區的中點也豎直對齊,把文本的 baseline 相對行盒 baseline 稍微下移來實現效果。結果是文本和緊挨著的小圖標漂亮地居中了
行盒 baseline 的移動
這是個用 vertical-align 的常見陷阱:行盒的 baseline 受該行所有元素的影響。我們假設有個元素以這種方式對齊(相對自身 baseline 對齊),行盒的 baseline 就不得不移動。因為大多數豎直對齊(除了 top 和 bottom)都是相對其 baseline 的,導致該行所有其它元素也都跟著調整位置
一些示例:
- 如果一行有個高元素橫跨整個高度,
vertical-align對它就不起作用了,它頂部之上和底部之下已經沒有能供它移動的空間了。為了滿足其相對行盒 baseline 的對齊關係,行盒 baseline 就不得不移動了。矮方塊具有vertical-align: baseline,左邊,高方塊是text-bottom對齊,右邊是text-top對齊,可以發現 baseline 帶著矮盒子一起跳上去了
<!-- 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(右圖)也會移動 baseline,這就怪了,因為根本不牽扯 baseline 啊
<!-- 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>
- 一行裡放兩個大元素,豎直對齊它們會移動 baseline 到滿足它們對齊方式的位置,然後行盒的高度也會調整(左圖)。添上第三個元素,其對齊方式不會讓它超出行盒的邊界的話,既不影響行盒的高度也不影響 baseline 的位置(中圖)。如果它超出了行盒的邊界,行盒的高度和 baseline 就會再次調整,這種情況下,我們最初的兩個方塊被推下去了(右圖)
<!-- 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 列表裡的 li 時,很容易遇到:
<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>
如圖所示,列表項坐在 baseline 上,baseline 下方是一些用於來容納文本下延部(descender)的空間,造成了間隙。解決方案呢?只需要把 baseline 移遠一點,例如,用 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>
這種場景不會出現在含有文本內容的內聯 - 塊元素中,因為 內容已經移到 baseline 上了
內聯級元素之間的間隙破壞佈局
這主要是內聯級元素自身的問題,但因為它們是 vertical-align 的依賴項之一,所以最好了解清楚
在前一個例子中也能看到列表項之間的間隙,間隙來自出現在標記代碼(HTML/XML 等)裡的內聯元素之間的空白字符。內聯元素之間的所有空白字符都被合併成一個空格,就是這個空格礙事,例如想讓兩個內聯元素僅挨在一起並都設置 width: 50% 的話,就沒有足夠的空間容納兩個 50% 的元素和一個空格。所以會拆分成 2 行破壞佈局(左圖)。為了去掉間隙,我們需要去掉空白字符,例如用 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 揭秘
嗯,就是這樣,一旦知道規則後就不很複雜。如果 vertical-align 不生效,只用考慮這些問題:
-
行盒的 baseline 和頂邊底邊在哪裡?
-
內聯級元素的 baseline 和頂邊底邊在哪裡?
這將揭示問題的解決方案
二、技巧
1. 怎樣確定行盒的 baseline?
給這一行加個沒有下延部的字符,一般習慣加 x,字符的底部邊緣就是行盒 baseline 的位置
例如:
.baseline:before {
content: 'x';
}
2. 怎麼確定行盒的邊界?
利用上面提到的「元素的 outer edge 相對行盒的 outer edge 對齊」:
.line-box-top {
border-top: 1px dotted red;
/* 讓 border-top 與行盒的頂邊重合 */
vertical-align: top;
/* 寬度沾滿整行 */
display: inline-block;
width: 100%;
/* 讓開空間,避免影響內容佈局 */
margin-right: -100%;
/* 提升 z,避免被內容遮住 */
position: relative;
z-index: 10;
}
/* .line-box-bottom 與之類似 */
在想要明確行盒邊界的那一行的行首(因為用 margin-right: -100%,所以放最左邊)添上
<span class="line-box-top"></span><span class="line-box-top"></span>
即可
3. 怎麼確定文本盒的邊界?
與確定行盒邊界的方法類似,利用 vertical-align: text-top; 和 vertical-align: text-bottom;
相對誰對齊,那麼就能把這個「誰」畫出來
4. 用 HTML 注釋去掉空白字符技巧
例如:
<figure>
<span class="large font">
<span class="green dotted line text-top"> </span><!--
--><span class="green dotted line text-bottom"> </span><!--
--><span class="red dotted line top"> </span><!--
--><span class="red dotted line bottom"> </span><!--
--><span class="blue dotted line baseline"> </span><!--
--><span class="font color-grey inline-overlay">x</span><!--
--><span class="center">
<span class="middle bg-grey">This</span>
<span class="tall box bg-grey text-top"> </span>
<span class="top bg-grey">can</span>
<span class="tall box bg-grey text-bottom"> </span>
<span class="bottom bg-grey">happen.</span>
</span>
</span>
</figure>
去掉空白字符的同時,保留標籤縮進格式,很有意思
暫無評論,快來發表你的看法吧