垂直對齊:你須要知道的全部事情(翻譯)

44年前咱們就把人類送上了月球, 但如今咱們仍然沒法在CSS中實現垂直居中。css

————James Anderson 2013/7/20html

注: 由於掘金不支持自定義樣式,文中基本全部的圖例都沒法正常顯示,請諸位讀者到個人博客上閱讀git

前言

原文地址github

Yeah😋, 讓咱們來討論一下vertical-align這個CSS屬性。 它常常被用於對齊彼此相鄰的文本和元素。就像將圖標居中與和它相鄰的文本app

可是它有時候看起來像一個SB,由於它全部讓人摸不着頭腦的規則都在起做用。 舉個例子, 可能會發生這樣的狀況, 你改變一個元素的vertical-align屬性以後, 它的對齊方式徹底沒有改變。 可是與其相鄰的其餘元素髮生了改變! 這是在逗我呢!ide

因此,爲了儘可能減小未來的痛苦,我研究了W3C的CSS specifications, 以便搞清楚vertical-align的行爲。佈局

你將會在這篇文章中學到什麼字體

  1. 內聯級(inline-level)元素在線框(Line Box)中的垂直居中行爲。
  2. 內聯級元素和線框具備基線(Baselines),頂部(Tops)和底部(Bottoms)。
  3. 用vertical-align對齊基線,頂部和底部。
  4. 示例:如何將圖標相對與相鄰的文本垂直居中。
  5. 示例:基線如何移動。
  6. 示例:如何使垂直居中的元素底部沒有空隙。
  7. 示例:如何消除兩個對齊的元素之間的空隙。

內聯級元素在線框中的垂直居中行爲

vertical-align被用於對齊內聯級元素 他們的display屬性的計算結果爲:ui

  • inline
  • inline-block
  • inline-table(未包含在這篇文章中)

Inline elements are basically tags wrapping text.this

內聯塊級元素如他們的名字通常:存在於行內的塊級元素。 他們擁有widthheight(或者由本身的內容決定)以及paddingbordermargin

內聯級元素以行的形式排列在一塊兒。 當元素過多沒法排列在一行以內, 就會在其下方建立一個新的行。 全部的行會有一個所謂的線框(Line Box), 它包含了行的全部內容。

**注:**這裏指的是每一行都有一個線框,而不是相鄰的行共用一個線框。

線框的高度受其內容的大小影響。 在下面的插圖中, 線框的頂部和底部用紅色的虛線表示。(原文插圖

一行 高的文本。
一行 矮的文本。
這也 有可能 發生。

在這些線框內, vertical-align負責對齊每一個元素。 因此, 什麼是元素對齊?

關於基線(Baselines),頂部(Tops)和底部(Bottoms)

垂直對齊最重要的參照點是所涉及元素的基線。 在某些狀況下, 元素的封閉盒(enclosing box)的上下邊緣也很重要。 讓咱們來看看每一個相關類型元素的基線和外邊緣的位置。

內聯級元素

aAÄ qQ
aAÄ qQ
aAÄ qQ

如今你能看到相鄰的三行文本。 紅線表示行高的上下邊緣, 綠線表示字體的高度, 藍線表示基線。

  • 最左側文本的行高與字體大小相等, 所以表明字體高度的綠線和表明行高的紅線重疊成了一條線。
  • 中間的文本的行高是字體大小的兩倍。
  • 右側的文本的行高是字體大小的通常。

內聯級元素的外邊緣與其行高的上下邊緣對齊。 行高小於字體的大小可有可無。 因此在上圖中, 紅線表示的是外邊緣。

內聯級元素的基線, 字符位於其中。 在圖中由藍線表示。 粗略的講, 基線位於字體高度中間的某個位置。 詳細信息見W3C 規範

inline-block元素

C
C

從左到右你能看到:一個具備內容(一個「C」)的inline-block元素, 一個具備內容且overflow: hiddeninline-block元素, 一個沒有內容(可是指定了height)的inline-block元素。 紅色表示外邊距, 黃色表示邊框, 綠色表示內邊距, 藍色表示內容。 在每一個inline-block元素中基線都用藍線表示。

inline-block元素的外邊緣是其外邊距頂部和底部的邊緣。 在圖例中用紅線表示。

inline-blick元素的基線取決於元素是否有in-flow內容:

  • 在具備in-flow內容的狀況下, inline-block元素的基線是正常流(normal flow)中最後一個內容元素的基線(圖例中左側的例子)。 最後一個元素根據本身的規則找到本身的基線。
  • 在具備in-flow內容且overflow屬性的計算結果不是visiable的狀況下,基線是外邊距的底部邊緣(圖例中中間的例子)。 因此,它跟inline-block元素的底部邊緣相同。
  • 若是不存在in-flow內容, 基線也是外邊距的底部邊緣(圖例中右側的例子)。

Line Box

This can happen.

你已經在上面看到過這個圖例了。 此次我也畫出了線框的文本框的上下邊緣(綠線)和基線(藍線)。 還給文本元素加上了灰色的背景以突出其區域。

線框的頂部於最頂部元素的頂部對齊, 線框的底部於最底部元素的底部對齊。 這在上面的圖例中以紅線所示的方框表示。

線框的基線是可變的:

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

CSS 2.1沒有定義線框的基線位置。

— the W3C Specs

vertical-align這個屬性上,基線(Baseline)大概是最混亂的部分。 It means, the baseline is placed where ever it needs to be to fulfil all other conditions like vertical-align and minimizing the line box’s height. It is a free parameter in the equation.

由於線框的基線是不可見的,所以它並非顯而易見的。 可是咱們能很容易地使它可見。 只須要在行的開頭添加一個字符, 好比我在圖例中添加了一個「x」。 若是這個字符沒有對齊任何地方, 默認狀況下會位於基線上。

文本框(Text Box)

咱們將線框基線周圍的內容稱之爲文本框。 文本框能夠簡單地看做線框內沒有任何對齊的inline元素。 它的高度等於父元素的字體大小。 所以, 文本框僅僅包含線框的無格式文本。 該框在上面的圖例中用綠線表示。 由於文本框於基線相關聯, 當基線移動時文本框也會移動。

注: 這個文本框在W3C規範中被稱爲strut

啊,這是最難的部分了。 如今咱們知道了一切, 要把事情弄清楚,讓咱們快速總結一下最重要的部分:

  • 有一個區域叫作線框(Line Box),這是發生垂直對齊的區域。 他有一條基線(Baseline), 一個文本框(Text Box)和上下邊緣。
  • 內聯級元素。 它們是對齊的對象, 它們有一條基線和上下邊緣。

用vertical-align對齊基線,頂部和底部

By using vertical-align the reference points mentioned in the list above are set into a certain relationship.

相對於線框的基線對齊

x baseline sub super -50% +10px
  • baseline: 元素的基線剛好於線框基線的重疊。
  • sub: 元素的基線位於線框基線下方。
  • <percentage>: 元素的基線相對於線框的基線移動的距離是相對於行高的百分比。
  • <length>: 元素的基線相對於線框的基線移動的距離是一個絕對值

一個特例是vertical-align: middle,以下圖例:

x middle
  • middle: 元素上下邊緣的中點於線框基線加上x高度的一半對齊。

相對於線框的文本框對齊

x text-top text-bottom

也能夠在相對於線框的基線對齊的狀況下列出這兩個案例, 由於文本框的位置由基線決定(見文本框(Text Box))。

  • text-top: 元素的上邊緣於線框中文本框的上邊緣對齊。
  • text-bottom: 元素的下邊緣於線框中文本框的下邊緣對齊。

相對於線框的外邊緣對齊

x top bottom
  • top: 元素的上邊緣於線框的上邊緣對齊。
  • bottom: 元素的下邊緣於線框的下邊緣對齊。

固然W3C中也有正式的定義

Why Vertical-Align Behaves The Way It Behaves

咱們如今能夠在某些狀況下仔細地研究垂直對齊了。咱們還會處理一些可能出問題的狀況。

居中一個圖標

一個困擾個人問題以下:我有一個圖標, 我想在一行文本旁邊居中放置它。 僅僅給圖標設置vertical-align: middle彷佛不是一個使人滿意的居中方案。 看看這個示例:

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

這讓咱們的問題有些苗頭了。 由於左邊的文本徹底沒有對齊圖標, 它跟基線對齊了。 如今的狀況是,經過給圖標設置vertical-align: middle咱們將圖標的中點和小寫字母「x」的中點(即x-height的一半)對齊。因此有升部和降部的字符就會突出。

注: 升部和降部分別指字母向上超出主線和向下超出基線的部分。來自維基百科

注: x-height(譯名:X高度)是一個特有名詞,是指字母的基本高度,精確地說,就是基線(英語:baseline)和主線之間的距離。特別的,它指稱一個字體中小寫字母x的高度(這也是這個詞的語源)。來自維基百科

在右邊, 咱們使整個字體區域對齊垂直方向上的中點。 這時文本的基線向線框的基線的下方移動了一點以對齊中點。 很明顯這是一個完美的對齊的方案。

線框基線的移動

使用vertical-align時, 有一個常見的陷阱:基線的位置會被行內全部的元素影響。 讓咱們作一個假設, 當一個元素以某種方式對齊時, 線框的基線必定會移動。 大多數垂直對齊(除了頂部和底部對齊)都是相對於基線對齊的, 這會致使線框內的其餘元素從新調整其位置。

看看這些示例:

  • 若是一個元素的高度等於整行的高度,vetical-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屬性取其餘值, 也會顯示如上相同的行爲。

  • 即便設置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屬性爲middle, 且上下都沒有超過線框的邊緣。 所以既不會影響線框的高度也不會影響線框基線的位置。 若是它超過了線框的邊緣, 那麼線框高度和基線又會移動了。 在這種狀況下, 前面兩個元素被下來了。
<!-- left mark-up -->
<span class="tall-box text-bottom"></span>
<span class="tall-box text-top"></span>

<!-- middle mark-up -->
<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-level)元素下方可能會有一點小空隙

看看這個示例。 若是你嘗試垂直對齊列表中的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>
複製代碼

如你所見, 列表項位於基線上。 基線下方留出了一些空間來容納文本的降部。 這正是咱們看到空隙的緣由。 如何解決這個問題? 既然文本是與基線對齊才留出了空隙,那麼咱們只須要將文本與其餘的位置對齊就行了。 舉個例子, 將列表項設置爲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>
複製代碼

具備內容的inline-block元素不會發送這種狀況,由於基線已經位於內容的上方了

內聯級元素之間的空隙會破壞佈局

這主要是內聯級元素自身的問題, 可是由於這是垂直對齊的一個要求,所以瞭解這一點是很好的。 (不太理解這句話, 下面是原文。)

This is mainly a problem of inline-level elements themselves. But since they are a requirement of vertical-align, it is good to know about this.

你能在第一個示例中的列表項看到空隙。 空隙來自內聯元素之間的空白。 內聯元素之間的全部空白被合併爲一個空格。 若是咱們並排放置兩個內聯元素而且給它們的width屬性賦值50%。那麼它們所佔據的空間就是兩個50%加上一個空格。 顯然這超出了一行。 所以該行被分爲兩行, 這破壞了佈局(左側)。 若是要消除空隙,咱們就須要刪除空格,例如使用html註釋(右)。

50% wide
50% wide
50% wide
50% wide
<!-- 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>
複製代碼

垂直對齊揭祕

yea😋,就是這樣。 若是你知道規則,它並不複雜。 若是vertical-align的表現與預期的不一致, 就問問本身下面這些問題:

  • 線框的基線位置以及上下邊緣的位置在哪?
  • 內聯級元素的基線位置以及上下邊緣的位置在哪?

這些問題有可能幫助你解決遇到的難題。

這裏有一個更復雜的示例如何垂直居中一個vertical-align屬性爲middlediv

相關文章
相關標籤/搜索