由於一次內聯元素錯位引發的line-height思考

李多睿發表於2018-10-25

line-height對於一個前端小可愛來說,應該是一個會經常碰面的老朋友了。可是有一天,我突然發現自己好像對他沒那麼瞭解,他也沒有外表看起來的那麼簡單。

事情的經過是這樣的……

在偶然一次工作中,我寫了這樣的模板:

<div>
    <span class="name">重大疾病險</span>
    <span class="tip">保額每月可累計</span>
</div>
複製程式碼
div{
    font-family: "PingFang SC";
}
.name{
    line-height: 20px;
    font-size: 20px;
}
.tip{
    display: inline-block;
    line-height: 20px;
    font-size: 14px;
}
複製程式碼

兩個相鄰的內聯元素,字型一大一小,行高相同,由於第二段文字需要有字多情況就自動去下一行的效果,所以第二個spaninline-block我暢想的結果是,兩個span高度都是20px,div高度也是20px,多麼完美。但結果往往不近人意……

由於一次內聯元素錯位引發的line-height思考
div的高度怎麼是28??

由於一次內聯元素錯位引發的line-height思考 由於一次內聯元素錯位引發的line-height思考

再一看子元素,一個28一個20??

疑問1:難道是line-height對行內元素不生效??
  然而規範告訴我,對於非替代的inline元素,它用於計算行盒(line box)的高度。

疑問2:既然生效了,為什麼審查元素看著是28,不是20呢?
  對於文字來說,存在一個內容區域(content area)。你可以理解為,用游標選中這行文字時帶背景的區域。他同時受font-family和font-size的影響。就算是相同的字號,如果字型不同,‘你所看到’的高度也是不一樣的,同學們可以自己嘗試一下。這裡強調你所看到的,也就是我們這裡的28px,它其實是這個文字內容區的高度,而內容區的高度並不是真正的高度,也就是說,它不會影響這個元素真正的尺寸,也不會撐起父元素的高度,所以只是一個你看起來的高度。而對於行內元素來說,真正影響它高度的就是line-height
  在這裡,我們可以通過改變他們的font-family來證實一下。

div{
    font-family: "HelveticaNeue";
}
.name{
    line-height: 20px;
    font-size: 20px;
}
.tip{
    display: inline-block;
    line-height: 20px;
    font-size: 14px;
}
複製程式碼
由於一次內聯元素錯位引發的line-height思考 由於一次內聯元素錯位引發的line-height思考 由於一次內聯元素錯位引發的line-height思考

可以看到,更改了字型之後,.name的高度由28變成了24,.tip由於是inline-block所以高度沒變,但是父div的高度依然是28px,說明content area並不會影響父元素的真實高度。

疑問3:那如何看到它真正的高度?
  你可能也發現了,第二個span是inline-block,它的高度就是20px,那我們將第一個span也設定成inline-block來看一下。
由於一次內聯元素錯位引發的line-height思考
果然,它的高度終於變成了我們期望的20px。
那我們再來看一下父元素div的高度:
由於一次內聯元素錯位引發的line-height思考
!!怎麼還是28px??

疑問4:兩個子元素都已經是20px了,為什麼父元素還是28px?
這裡先宣告兩個問題:
(1)證明前面疑問2中說的沒錯,content area是不會影響實際尺寸的,現在我們元素都是20px了,可父元素依然還是28px。
(2)我們將第一個span設定成inline-block之後,可以看到它的尺寸已經變成了20px,但這並不代表這它就沒有content area了,或者它的content area變成了20px。你可以理解為我們給他外面包了一層盒子,你可以看到盒子的真正的尺寸,而content area在它裡面,你只是看不到罷了。更何況,它是一個影響不了誰的東西。

接著回到我們的問題,仔細看上圖,可以發現,兩個高度都是20px的子元素,上下都有一定的間隙;再仔細看,發現它倆沒對齊,紅色背景的靠上,粉色背景的靠下,它倆之間稍微有些錯位。就是這些,讓我們的父元素被撐成了28px。那它們又是從哪裡來的呢?

既然是對不齊導致父元素被撐高了,那css中相關對齊的屬性,我們很容易想到是vertical-align,難道是它搞得鬼?那我們試著改一下它們的vertical-align

由於一次內聯元素錯位引發的line-height思考

vertical-align:top,上面對不齊了
由於一次內聯元素錯位引發的line-height思考

vertical-align:middle,好像跟之前差別不大……

雖說這些屬性值都沒能讓他們對齊,但可以發現確實是與它有關的,可能是我們哪裡使用不當,影響了這個屬性?那我們就去了解一下vertical-align是怎麼對齊的,起初我們不設定的時候,會取它的預設值:baseline—— 基線對齊。
內聯元素預設是基線對齊的,而基線就是指行框盒子中字母'x' 的下邊緣:

由於一次內聯元素錯位引發的line-height思考

誒??再仔細看看我們之前對不齊的那張圖,你會發現,雖然元素沒對齊,但是文字下端是對齊的,這不就是正常的基線對齊嗎!這下就說的通了。

劃重點!!
行內元素的對齊方式預設是基線對齊,也就是都與 x 的下邊緣對齊。這兩個span的高度是一樣的,都是20px,然而重點就在於它們的字型大小不一樣。而文字在如果設定了line-height,是會基於line-height居中的,也就是說,它們分別在各自高度相同框框中居中,但由於字號大小不一樣,所以文字底部是對不齊的,但他們又需要基線對齊,所以它倆開就必須要錯位一下。
這裡引用一下鑫旭大神的圖:

由於一次內聯元素錯位引發的line-height思考

疑問5:那這是不是就說明,我們把兩個元素的行高設定成不一樣,就可以不錯位把父元素撐高了?

由於一次內聯元素錯位引發的line-height思考

這裡我們分別給兩個span設定與自己字型大小一樣的行高:20px和14px,可以看到它倆已經沒有向外錯位了,但是父元素的高度並沒有如期,還是28px。怎麼回事?難道還有別的東西再影響它?

你猜對了,但這是個我們看不見的東西。引用一下張鑫旭大神的叫法——幽靈空白節點。
“幽靈空白節點”是內聯盒模型中非常重要的一個概念,具體指的是:在 HTML5 文件宣告 中,內聯元素的所有解析和渲染表現就如同每個行框盒子的前面有一個“空白節點”一樣。這 個“空白節點”永遠透明,不佔據任何寬度,看不見也無法通過指令碼獲取,就好像幽靈一樣, 但又確確實實地存在,表現如同文字節點一樣,因此,我稱之為“幽靈空白節點”。

既然看不見,那我們可以拿一個看的見的 x 字元來假裝它。

<div>
    <em>x</em>
    <span class="name">重大疾病險</span>
    <span class="tip">保額每月可累計</span>
</div>
複製程式碼
div{
    font-family: "PingFang SC";
}
em{
    display:inline-block; //為了審查元素看到的不是content area
    font-style:normal;
}
.name{
    line-height: 20px;
    font-size: 20px;
}
.tip{
    display: inline-block;
    line-height: 20px;
    font-size: 14px;
}
複製程式碼
由於一次內聯元素錯位引發的line-height思考

這個x字號和行高都沒有設定,字號是繼承下來的20px,元素行高預設是1.x(1.x倍的font-size),具體數值還有待研究,但絕對是>1的,由圖可見,什麼都沒有設定的情況下,這個x的高度就是28px,也就代表著,那個幽靈空白節點的高度就是28px。
那我們將幽靈空白節點的行高設小一些不就好了?可是這個幽靈節點,看不見摸不著,怎麼設啊。別忘了,line-heigt是可繼承性的,我們給父div設定line-heigt,幽靈節點自然就會繼承。

解決一
我們可以給父divline-height設定一個很小的值

div{
    line-height: 5px;
    font-family: "PingFang SC";
}
em{
    display:inline-block; //為了審查元素看到的不是content area
    font-style:normal;
}
.name{
    line-height: 20px;
    font-size: 20px;
}
.tip{
    display: inline-block;
    line-height: 20px;
    font-size: 14px;
}
複製程式碼
由於一次內聯元素錯位引發的line-height思考 由於一次內聯元素錯位引發的line-height思考

這樣,幽靈節點的行高就只有5px,父元素行高20px,沒毛病。但這個方法還不夠完美,因為你需要想一下這個很小的值……

解決二
當line-height設定為一個無單位的數值時,表示是某倍的font-size。

div{
    line-height: 1;
    font-family: "PingFang SC";
}
em{
    display:inline-block; //為了審查元素看到的不是content area
    font-style:normal;
}
.name{
    line-height: 20px;
    font-size: 20px;
}
.tip{
    display: inline-block;
    line-height: 20px;
    font-size: 14px;
}
複製程式碼

給父元素設定line-height為1,這樣,子元素高度都是自己的font-size,包括幽靈空白節點。這樣就不會再有人無意間撐高父元素了。當然,除非你的幽靈空白節點繼承下來了一個很大的font-size,這點你得明白。
由於一次內聯元素錯位引發的line-height思考 由於一次內聯元素錯位引發的line-height思考

解決三
既然行高可以與font-size有關,font-size也是可繼承的,那我們將父元素的font-size設為0

div{
    font-size: 0px;
    font-family: "PingFang SC";
}
em{
    display:inline-block; //為了審查元素看到的不是content area
    font-style:normal;
}
.name{
    line-height: 20px;
    font-size: 20px;
}
.tip{
    display: inline-block;
    line-height: 20px;
    font-size: 14px;
}
複製程式碼
由於一次內聯元素錯位引發的line-height思考 由於一次內聯元素錯位引發的line-height思考

可以看到,我們模仿的那個幽靈節點已經沒有了,寬高都是0了,父元素的高度也自然就回到了20px。

總結
以上,可以說是我對行內元素對齊問題的一些思考總結吧。如果你還有疑問,可以再多看幾遍,自己嘗試一下再繼續思考思考。這個問題我也是來來回回想了好多遍,也歡迎大家評論提問,我們可以一起討論~
前端小白,有講解錯誤或不全之處,還望大佬們嘴下留情,歡迎指正~

相關文章