完全瞭解vertical-align以及相關現象

爲何寫這篇文章

這一切都得從一個百思不得其解的問題提及。首先由於我一個錯誤的認知,覺得只要在一行裏設置了line-height,那麼這一行的文字都會居中顯示,這種效果若是在字號同樣的狀況下是正常的,可是在一行裏面若是有字號不同的文字,狀況就不同了。css

第二個X並無垂直居中,此時我想到了vertical-align這個偏方,試着對後面那個X設置了vertical-align:middle,因而神奇的事情發生了,後面那個字沒有像預期那樣上移動,反而向下移動少量。html

what???這麼神奇的嗎?到底是什麼魔力形成了這樣的效果?爲此我查閱了不少資料,終於理解這一現象的本質緣由,可是經我查閱的不少資料或多或少都說的不全、難以理解,甚至有一些錯誤,誤導你們。所以我決定結合自身的思考,寫一篇深刻淺出,通俗易懂的文章來徹底剖析涉及到vertical-align神奇現象的本質api

原理

正所謂授人授人以魚不如授人以漁,瞭解一個現象的本質,才能在往後再次遇到相似的問題時再也不迷惑。而我通過深思熟慮後,對此類現象有了一個結論:一般在line box裏遇到一些沒法理解的現象,一般都與font-family font-size line-height vertical-align四個屬性有關係。ide

能夠說這四個屬性是解釋line box不少沒法理解的表現的關鍵,在Deep dive CSS: font metrics, line-height and vertical-align這篇文章中詳細解釋了這個問題,這篇文章寫的很是好,很是推薦你們閱讀一遍,本文的一部分知識點都是來自於這篇文章,在文章裏面詳細描述的部分將會在本文中以我理解的方式來解釋。wordpress

那麼就來看看這四個屬性的相關原理。字體

字體與字體大小

每個字都是由一個容器(em-square)包裹住的,而且這個容器內定義了ascender、descender、capital height、x-height等的字體度量。flex

這些字體度量能夠理解成上圖中的每一條線在容器中的位置,每個字都是有本身的高度的,這能夠解釋爲何那些明明是空白的地方爲何也屬於文字高度計算範圍以內。this

在上圖中能夠看到x被選中時,包含了那些空白的地方,這些其實都是在字體的容器之中的。像這種沒有設置特定line-height的文字高度,咱們叫它爲內容區高度(the height of the content area)spa

The height of the content area should be based on the font, but this specification does not specify how.

內容區高度是與font-family有着千絲萬縷的關係,由於不一樣的font-family的有着不同的字體度量,這將會致使font-size相同而font-family不一樣的字的內容區高度不同。3d

如上圖所示,不一樣字體的內容區高度不同。因爲不一樣字體下設置的字體度量不同,若是都是從內容區頂部開始排列文字,那上圖這三個X確定是良莠不齊,因此這些字都是按照基線(baseline)排列的,一個字體容器的基線的位置能夠認爲是X的底部。上圖中三個X的底部都是在一條線上,這是由於它們此時都處於line box的基線上,同時也是由於它們此時的vertical-align屬性都是baseline

line-height

不知道你們有沒有想過爲何設置行高好像能夠垂直居中一段文字?
這須要先解釋一下line-height值是怎麼計算的。在CSS2官方定義文檔中中有詳細的描述,簡單來講就是將行高的計算值減去內容區高度獲得的值,各一半放在內容區上下方(這個計算出來的值能夠是負值)。這就能夠解釋爲何設置行高看起來好像是垂直居中。因爲文字在內容區中並無垂直居中,所以實際上只設置行高值是不能真正垂直居中一個字的,以下圖所示。

這裏能夠說個尚且在草案中的屬性leading-trim,這個屬性可使得計算的行高的時候是測量到大寫字母高度/字母基線而不是ascender/descender,使得文字在視覺上居中。

有一個問題,行高的默認值到底是多少?在那篇文章中有提到,因爲每種字體的設置字體度量不同,所以每一種字體默認行高值都不同,最低的到了0.618,最高甚至到了3.378。

on the 1117 fonts installed on my computer (yes, I installed all fonts from Google Web Fonts), 1059 fonts, around 95%, have a computed line-height greater than 1. Their computed line-height goes from 0.618 to 3.378. You’ve read it well, 3.378!

另外還要說一個很是重要的點,在CSS2官方定義文檔中有一個東西strut,文檔裏是這麼說的。

On a block container element whose content is composed of inline-level elements, 'line-height' specifies the minimal height of line boxes within the element. The minimum height consists of a minimum height above the baseline and a minimum depth below it, exactly as if each line box starts with a zero-width inline box with the element's font and line height properties. We call that imaginary box a "strut." (The name is inspired by TeX.).

簡單理解就是line box裏好像有一個0寬度的字(繼承父元素屬性)存在,strut對於理解那些奇怪的現象相當重要。

vertical-align

要談vertical-align,不得不先說它幾個重點值的定義,vertical-align用很差,很差用的緣由之一就是不清楚它的值具體是什麼意思,甚至於有些問題在看完具體的定義以後就能明白爲何。

  • baseline:把盒的基線與父級盒的基線對齊。若是該盒沒有基線,就把下外邊距邊界和父級的基線對齊。
  • middle:把該盒的垂直中點與父級盒的基線加上父級的半x-height對齊
  • text-top:把該盒的頂端和父級的內容區的頂端對齊
  • text-bottom:把該盒的底端和父級的內容區的底端對齊
  • top:把對齊子樹的頂端與行框的頂端對齊
  • bottom:把對齊子樹的底端與行框的底端對齊

middle值中說的半x-height其實能夠理解爲盒中直接寫一個沒有標籤包裹的x字母的一半高度,這個只受到父盒樣式影響的x其實能夠看做strut的有形表現,在後面的實驗中也是常常將這個用做strut來查看基線的位置以及strut內容區高度的大小。其餘的值的定義就說的很清晰,無需做過多解釋。
那麼說完值以後再說一下vertical-align規定,一樣也是看完以後就可以理解一些問題的緣由。

  1. vertical-align的值只對父級行內元素或者一個父級塊容器元素的strut有效。
  2. 在上面值的定義中,對於行內非替換元素,用於對齊的盒是那個高度爲line-height(包括該盒的內容去和兩邊的半行距)的盒。對於其它全部元素,用於對齊的盒都是外邊距框。
  3. inline-block(盒)的基線是它的最後一個常規流中的行框的基線。
  4. 若是行框沒有流內行框或者其overflow屬性的計算值不爲visible,此時行框的基線是下外邊距邊界。

第一條只對行內元素有效的規定想必你們都清楚,可是一樣對stuct有效這一點,我想知道怎樣才能對它設置vertical-align的值,等評論大神指點一下。

第三點我的認爲是有問題的,據我所知,設置了vertical-align的元素仍在常規流中,那麼第一個問題:假設最後一個常規流中的行框設置了vertical-alignmiddle,此時它根據上述middle值的定義,須要將該盒的垂直中點與父級盒的基線加上父級的半x-height對齊,但若是根據第三條規則所說,此時將產生矛盾。

第二個問題是:假設盒內只有一個行框,但此時此行框的vertical-align屬性的值爲middle,那麼此時父盒的基線在哪?

所以我認爲此第三條規則應該是這樣:inline-block(盒)的基線是它的最後一個常規流中而且vertical-align屬性爲baseline的行框的基線,若是盒中的行框的vertical-align屬性都不爲baseline,那麼基線位置由strut肯定。

第四條規定咱們看個圖就明白了。

圖中灰色線條是我畫的基線所在位置,第二個行框內是空的,所以此時它的基線位置是它的下外邊距,因此纔會形成這樣圖中這樣奇特的狀況出現。

另外,行內替換元素的基線位置就須要注意一下。

能夠看到圖中的imgtextareavideo元素的基線都是下外邊距,而selectinput元素的基線都是其中的文字基線。這裏又能夠看到一個神奇的現象,那就是若是拉扯textarea的右下角,將它向下拉扯,textarea的高度會變大沒錯,可是因爲基線須要與父盒基線對齊,因此實際上textarea是向上增加的。

現象解析

正所謂學以至用,有了上面打下的基礎,如今跟着我一塊兒來分析那些神奇的現象吧。

1.第一個固然是我遇到的問題:設置相同的行高狀況下,對後面那個X設置vertical-alignmiddle,卻下移了。

其實這個還挺好理解的,vertical-align屬性的middle值中的定義加上第二條規定就能解釋了,就是第二個行框高度的中線與第一個行框的基線加上X高度的一半(可看做x的中點)對齊,畫個線讓大家看的更明白。

因爲第二個行框中的x在行框中不是垂直居中的(由於X在內容區中不是居中的),因此最終致使了這樣的現象。

2.設置了vertical-align沒有效果。

首先第一個條件是行內元素須要知足,若是不知足那確定沒有效果。
若是知足了行內元素的要求,那麼我認爲其實不存在沒有效果的狀況,一般這種狀況是修改值後的位置和修改前的一致,因此看起來好像沒有效果,但其實是生效了,我上個圖展現某一種無效的狀況。

圖中第二個行框設置了vertical-alignmiddle,而後第二個行框中的字號不斷變化,變化的過程當中能看到,出現了兩個基線在同一條線上的狀況,看起來好像是在同一條基線上,即就是好像與vertical-alignbaseline的狀況一致。所以若是遇到了設置vertical-align後沒有變化的狀況,靜下心來用我上面提供的原理進行分析,確定能找到緣由。

3.行框設置了line-height後,行框實際高度大於line-height

其實這個問題本質和張鑫旭文章中提到的幽靈空白節點是同樣的,這類問題的本質就是line box中不可見的strut在做妖,這個strut會繼承父盒的樣式,像line-heightfont-sizefont-family等等都是能夠繼承的樣式,而正如我所說,這些樣式是致使這些現象的關鍵因素。話很少說,分析一個例子。

如圖所示,在外層div設置了line-height:50px,同時給子行框設置font-size:50px,這樣卻致使了實際高度大於咱們預期的50px,爲何就這樣呢?是的,就是strut在做妖,因爲沒有在外層div設置字號,所以strut的字號屬性繼承的是外層div,此時strut的字號是默認字號,而默認字號的內容區高度比50px的內容區高度小,同時二者的都是基於基線對齊的,根據line-height的計算方式,strut下面分配到高度增長,從而致使下方出現了空白,我寫一個不帶樣式的X來展現一下就明白了。

左邊的x淺藍色部分就是它的內容區高度,而黑色部分則是因爲設置了line-height後計算分配獲得的高度,注意看上下黑色高度是同樣的。看到這裏就明白了,引發這些神奇現象的魔力之源就是這個strut以及那四個樣式。

那麼如何去掉這些空白,因爲沒法直接操做到strut,那麼只能對那四個屬性下手了。首先得明白解決此類問題的核心思路:消去無形strut的影響stuct是怎麼影響到咱們的呢?那是由於它是有一個具備高度的東西,而且與baseline定位有關。那麼具體的解決思路便有二:1.消去它高度的影響。2.消去它定位的問題。

如何消去strut的高度呢?其實這個問題就是如何使一個文字失去高度,這個問題想必你們都清楚,讓font-sizeline-height變爲0(若是父盒設置了line-height固定值再設置font-size爲0,strut仍是有高度的)。

(如下分析皆基於上圖分析,且將左邊行框看成strut
那麼怎麼消去它定位的問題呢?固然是靠vertical-align這個屬性了,其實網上的文章都有寫到設置vertical-align的值能夠去掉空白部分,可是都沒有仔細說說爲何設置完就能消掉,這樣就容易出現問題。比方上圖的狀況,對後面的X設置vertical-alignmiddle並不能消掉空白,你們回想一下middle的定義,想象一下設置以後的效果。結果就是,右邊的x下移,空白部分到了上方。由於設置middle值後是右方行框中點去對齊左方X的中點,而左方X的中點是在左方行框中間偏下的地方,因此像這種狀況設置middle是消除不掉空白的。

那麼你們認爲text-top/text-bottom能消掉空白部分嗎?再來回想這兩個值的定義,若是想的起來話答案固然就脫口而出:不能。由於這兩個值是讓右邊的行框去對齊左邊行框內容區的頂部或者底部,即淺藍色的頂部或者底部。

那麼另外兩個經常使用的top/bottom能夠消掉這些空白嗎?答案是能夠的,這兩個值的定義就是對齊父盒行框中最高或者最低點,因爲兩個行框的高度一致,因此是能夠對齊的。可是呢,若是右邊的行框高度小於左邊行框的高度,還能消去那空白嗎?若是左邊行框的高度小於右邊行框的高度,表現出來的樣式一致嗎?(這兩個問題但願讀者能夠採用我下面的方法來進行猜測並驗證)

這裏要重點提一個我的觀點:行框內全部vertical-align不爲baseline的元素一開始不會進入位置的計算,在baseline肯定後,再進入定位,定位後行框從新計算樣式。或者這個觀點能夠做爲分析的方法,上述的問題的例子都比較小,若是行框中的元素變多,且各自的垂直定位的值都不同,分析起來就比較困難。可是若是用個人這個觀點來分析則比較容易,而且結果也證明此觀點無問題。

總結

若是有人能看到這裏,但願你已經明白這些現象的本質所在,而且在之後遇到了這些問題,都能貽笑大方的輕鬆解決。我在編寫這篇文章時查閱了大量資料,可是有不少東西在官方文檔裏也只是隻言片語帶過,所以誕生了不少自個人觀點,若有錯誤,歡迎指點。

如下是個人我的觀點的總結:

  1. 一般在line box裏遇到一些沒法理解的現象,一般都與font-family font-size line-height vertical-align四個屬性以及strut有關係。
  2. inline-block(盒)的基線是它的最後一個常規流中而且vertical-align屬性爲baseline的行框的基線。若是盒中的行框的vertical-align屬性都不是baseline,那麼基線位置由strut肯定。
  3. 行框內全部vertical-align不爲baseline的元素一開始不會進入位置的計算,在baseline肯定後,再進入定位,定位後行框從新計算樣式。)
  4. 消除這些現象的本質是消除strut的影響以及理解vertical-align屬性的值的定義。

(能不用vertical-align就不用吧,flex大法好)

相關文章
相關標籤/搜索