一次內聯元素錯位引發對line-height的思考
作者:李一睿
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;
}
複製程式碼
兩個相鄰的內聯元素,字型一大一小,行高相同,由於第二段文字需要有字多情況就自動去下一行的效果,所以第二個span
是inline-block
我暢想的結果是,兩個 span 高度都是 20px, div 高度也是 20px,多麼完美。但結果往往不近人意……
div 的高度怎麼是 28??
再一看子元素,一個 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; } 複製程式碼
可以看到,更改了字型之後,
.name
的高度由28變成了24,.tip
由於是inline-block
所以高度沒變,但是父div
的高度依然是28px,說明content area並不會影響父元素的真實高度。 -
疑問3:那如何看到它真正的高度?
你可能也發現了,第二個 span 是
inline-block
,它的高度就是 20px,那我們將第一個span
也設定成inline-block
來看一下。
果然,它的高度終於變成了我們期望的 20px。
那我們再來看一下父元素div
的高度:
!!怎麼還是 28px?? -
疑問4:兩個子元素都已經是 20px 了,為什麼父元素還是 28px?
這裡先宣告兩個問題:
- 證明前面疑問2中說的沒錯,content area 是不會影響實際尺寸的,現在我們元素都是 20px 了,可父元素依然還是 28px。
- 我們將第一個 span 設定成
inline-block
之後,可以看到它的尺寸已經變成了 20px,但這並不代表這它就沒有 content area 了,或者它的 content area 變成了 20px。你可以理解為我們給他外面包了一層盒子,你可以看到盒子的真正的尺寸,而 content area 在它裡面,你只是看不到罷了。更何況,它是一個影響不了誰的東西。
接著回到我們的問題,仔細看上圖,可以發現,兩個高度都是 20px 的子元素,上下都有一定的間隙;再仔細看,發現它倆沒對齊,紅色背景的靠上,粉色背景的靠下,它倆之間稍微有些錯位。就是這些,讓我們的父元素被撐成了 28px。那它們又是從哪裡來的呢?
既然是對不齊導致父元素被撐高了,那 CSS 中相關對齊的屬性,我們很容易想到是
vertical-align
,難道是它搞得鬼?那我們試著改一下它們的vertical-align
:vertical-align:top
,上面對不齊了
vertical-align:middle
,好像跟之前差別不大……雖說這些屬性值都沒能讓他們對齊,但可以發現確實是與它有關的,可能是我們哪裡使用不當,影響了這個屬性?那我們就去了解一下
vertical-align
是怎麼對齊的,起初我們不設定的時候,會取它的預設值:baseline
—— 基線對齊。內聯元素預設是基線對齊的,而基線就是指行框盒子中字母'x' 的下邊緣:
誒??再仔細看看我們之前對不齊的那張圖,你會發現,雖然元素沒對齊,但是文字下端是對齊的,這不就是正常的基線對齊嗎!這下就說的通了。
劃重點!!
行內元素的對齊方式預設是基線對齊,也就是都與 x 的下邊緣對齊。這兩個 span 的高度是一樣的,都是 20px,然而重點就在於它們的字型大小不一樣。而文字在如果設定了
line-height
,是會基於line-height
居中的,也就是說,它們分別在各自高度相同框框中居中,但由於字號大小不一樣,所以文字底部是對不齊的,但他們又需要基線對齊,所以它倆開就必須要錯位一下。這裡引用一下鑫旭大神的圖:
-
疑問5:那這是不是就說明,我們把兩個元素的行高設定成不一樣,就可以不錯位把父元素撐高了?
這裡我們分別給兩個
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; } 複製程式碼
這個 'x' 字號和行高都沒有設定,字號是繼承下來的 20px,元素行高預設是 1.x(1.x 倍的 font-size),具體數值還有待研究,但絕對是 >1 的,由圖可見,什麼都沒有設定的情況下,這個 'x' 的高度就是 28px,也就代表著,那個幽靈空白節點的高度就是 28px。
那我們將幽靈空白節點的行高設小一些不就好了?可是這個幽靈節點,看不見摸不著,怎麼設啊。別忘了,
line-heigt
是可繼承性的,我們給父div
設定line-heigt
,幽靈節點自然就會繼承。
如何解決
明白了上述的疑問以及原理,接下來就看下怎麼解決這個問題。
解決一
我們可以給父div
的line-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;
}
複製程式碼
這樣,幽靈節點的行高就只有 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, 這點你得明白。
解決三
既然行高可以與 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;
}
複製程式碼
可以看到,我們模仿的那個幽靈節點已經沒有了,寬高都是 0 了,父元素的高度也自然就回到了 20px。
總結
以上,可以說是我對行內元素對齊問題的一些思考總結吧。如果你還有疑問,可以再多看幾遍,自己嘗試一下再繼續思考思考。這個問題我也是來來回回想了好多遍,也歡迎大家評論提問,我們可以一起討論~
前端小白,有講解錯誤或不全之處,還望大佬們嘴下留情,歡迎指正~