1. 程式人生 > >CSS深入理解vertical-align和line-height基友關係的複雜現象

CSS深入理解vertical-align和line-height基友關係的複雜現象

為了讓任意個數的列表最後一行也是對齊排列,在列表最後會輔助列表等寬的空標籤元素來佔位,類似下面紅色高亮HTML程式碼:

.justify-fix { display: inline-block; width: 128px; }
<div style="text-align: justify;">
    <img src="img/mm1.jpg" width="128">
    <img src="img/mm1.jpg" width="128">
    <img src="img/mm1.jpg" width="128">
    <img
src="img/mm1.jpg" width="128">
<i class="justify-fix"></i> <i class="justify-fix"></i> <i class="justify-fix"></i> </div>

圖示意:

這裡寫圖片描述

同樣的,在白色背景下,似乎看上去效果還不賴,但是,如果給div容器加個背景色~~

這裡寫圖片描述

會驚訝的發現,下面多了很大一塊間隙.

為了便於大家看其究竟,我把佔位i元素outline高亮下,於是,效果如下:

這裡寫圖片描述

結果會發現,上面巨大的空隙是由佔位i元素上面和下面的間隙共同組成的。

下面問題來了:上面的間隙是如何產生的?下面的間隙是如何產生的?如果去除這些間隙呢?

很多時候,複雜問題是由簡單問題組合而成的,實際上,這裡的間隙現象的始作俑者和上面的簡單現象一樣,都是vertical-align和line-height搞基帶來的不好的影響。

按照之前問題解決方法,我們可以直接來個line-height:0解決垂直間隙問題:

div { line-height: 0; }

結果圖片和圖片之間的間隙是沒有了,但是,圖片和最後的佔位元素之間依然有個幾畫素的間距,,啊啊啊啊,這究竟是什麼鬼?

這裡寫圖片描述

簡單現象的背後往往有大的學問,接下來是本文的高潮了,究其原因,要說到inline-block元素和基線baseline之間的一些糾纏的關係。

⑤ inline-block和baseline

CSS2的視覺化格式模型文件中有一麼一段話:

The baseline of an ‘inline-block’ is the baseline of its last line box in the normal flow, unless it has either no in-flow line boxes or if its ‘overflow’ property has a computed value other than ‘visible’, in which case the baseline is the bottom margin edge.

直譯一下:
‘inline-block’的基線是正常流中最後一個line box的基線, 除非,這個line box裡面既沒有line boxes或者本身’overflow’屬性的計算值而不是’visible’, 這種情況下基線是margin底邊緣。

這段文件中出現了很多專有名詞line box, line boxes等,這些是內聯盒子模型中的概念,是CSS進階必備知識。
如果大家沒有足夠精力去學習之,可以先看下面這張圖:

這裡寫圖片描述

上圖引用張大神的

由於上面的譯文是直譯的,理解起來還是有些拗口,我使用通俗的話描述就是:一個inline-block元素,如果裡面沒有inline內聯元素,或者overflow不是visible,則該元素的基線就是其margin底邊緣,否則,其基線就是元素裡面最後一行內聯元素的基線。

納尼,還是沒反應過來?

那我們看下面這個例子,應該就知道什麼意思了。

兩個同尺寸的inline-block水平元素,唯一區別就是一個空的,一個裡面有字元,程式碼如下:

.dib-baseline {
  display: inline-block; width: 150px; height: 150px;
  border: 1px solid #cad5eb; background-color: #f0f3f9;
}
<span class="dib-baseline"></span>
<span class="dib-baseline">x-baseline</span>

結果,科科:
這裡寫圖片描述

會發現,明明尺寸、display水平都是一樣的,結果呢,兩個卻不在一個水平線上對齊,為什麼呢?哈哈,上面的規範已經說明了一切。第一個框框裡面沒有內聯元素,因此,基線就是容器的margin下邊緣,也就是下邊框下面的位置;而第二個框框裡面有字元,純正的內聯元素,因此,第二個框框就是這些字元的基線,也就是字母x的下邊緣了。於是,我們就看到了框框1下邊緣和框框2裡面字元x底邊對齊的好戲。框框2有個小彩蛋,點選可以toggle其innerHTML,會發現,如果框框2裡面沒文字,就和框框1舉案齊眉了。

下面我們要做一件很有必要的事情,用來幫助我們理解上面複雜例子在line-height值為0後的表現,什麼事情呢?哈,同境界模擬,我們也設定框框2的line-height值為0,於是,就會是下面這樣的表現:

這裡寫圖片描述

知道框框2為何又下沉了一點嗎?

因為字元實際佔據的高度是由行高決定的,當行高變成0的時候,字元佔據的高度也是0,此時,高度的起始位置就變成了字元content area的垂直中心位置,於是,文字就一半落在看看2的外面了。

由於文字字元上移了,自然基線位置(字母x的底邊緣)也往上移動了,於是,兩個框框的垂直落差就更大了。

OK,明白了上面的簡單例子,也就能明白上面的複雜例子。緊接著,如果我們在最後一個佔位的元素後面新增同樣的x-baseline字元,則:

這裡寫圖片描述

額~居然還有小夥伴皺眉頭,那我再用文字解釋下:
現在行高line-height是0, 則最後的x-baseline的垂直中線就和上面一列的圖片對齊,而基線呢,就在中線下面差不多半個x的高度地方,而這個高度落差就是最後圖片和容器的間隙高度值,因為前面的<i class="justify-fix">是個空元素,基線是自身的底部,哈哈,造業啊!

OK,一旦知道了現象的本質,我們就能輕鬆對症下藥了!要麼改造佔位<i>元素的基線、要麼改造“幽靈空白節點”的基線位置、要麼使用其他vertical-align對齊方式~

首先,來個最有意思的方法,對吧,改造佔位<i>元素的基線。這個很簡單,對吧,只要在空的<i>元素裡面隨便放幾個字元就可以了,例如,裡面有個x:

這裡寫圖片描述(這之前忘記加div的line-height:0;導致圖片下方有空隙。)

改造“幽靈空白節點”的基線位置,哈哈,使用font-size,字型足夠小時,基線和中線會重合在一起,什麼時候字型足夠小呢,就是0. 於是,CSS程式碼(line-height如果是相對值,line-height:0也可以省掉):

div { font-size: 0; }

這裡寫圖片描述

使用其他vertical-align對齊方式,就是讓兩端對齊的列表元素vertical-align:top/bottom/…之類。

div { line-height: 0; }
.justify-fix { display: inline-block; width: 128px; vertical-align: top; }

這裡寫圖片描述

恩恩,各種方法都完美解決了垂直間隙的問題,

基友關係暴露之後

這裡寫圖片描述

至此,vertical-align和line-height的斷背基友關係算是徹底暴露了,而且,從行為表現上來看,line-height是攻,vertical-align是個受。而很多內聯元素的行為表現,就是這對基友搞七搞八一起搞出來的。

以前,關係處於地下的時候,我們可能不會明白,為何男廁所的捲紙用得比女廁所還快;但是,現在關係暴露了,很多以前我們想不明白的事情一下子就豁然開朗了。

因此,我們要以正確地心態去看待這對好基友,畢竟,他們可以CSS屆非常重要的兩個主力大將。

本文牽扯的知識點甚多,建議大家如果想在重構領域有所造詣,很多基本的卻很深入的東西是很有必要弄透的。篇幅有限,有不少知識點都是一筆帶過的,大家若有疑問,可以自己去檢索與研究,例如,vertical-align各個值的規範解釋,內聯盒子模型,等等。也歡迎各種方式交流。

本文為原創文章,包含指令碼行為和樣式控制,會經常更新知識點以及修正一些錯誤,因此轉載請保留原出處,方便溯源,避免陳舊錯誤知識的誤導,同時有更好的閱讀體驗。
本文地址:http://www.zhangxinxu.com/wordpress/?p=4925