上一篇中提到自己 debug 時(shí)遇到一個(gè) css 知識(shí)盲點(diǎn):行高和字體的關(guān)系。隨后查了相關(guān)資料官份,在這里梳理下這個(gè)知識(shí),最后解釋下產(chǎn)生這個(gè)現(xiàn)象的原因。
現(xiàn)象
一般我們要讓文字在容器中產(chǎn)生垂直居中的效果肥惭,會(huì)把容器高度和行高設(shè)置成一樣,來達(dá)到這個(gè)效果(代碼紊搪,效果圖如下)蜜葱。
<div style="height:32px; line-height:32px; border:1px solid black;">
<span>Test<span>
</div>
但是當(dāng)容器的字體大小和文字的字體大小不一致的時(shí)候,字體就無法居中了(代碼耀石,效果圖如下)牵囤。
<div style="height:32px; line-height:32px; border:1px solid black; font-size:42px">
<span style="font-size:20px;">Test<span>
</div>
原理概述:行內(nèi)元素如何排版
在解釋上述現(xiàn)象之前,我們先來看下行內(nèi)元素(inline-element)在頁面上是如何排版的滞伟。我們先假設(shè)頁面上沒有任何我們添加的 css揭鳞,每個(gè)行內(nèi)元素會(huì)占據(jù)一定的矩形空間,我們稱之為行內(nèi)框(inline-box)梆奈,如下圖和代碼:
<div style="border:1px solid black; color:white;">
<span>span</span> <em>em</em> <strong>I am very very very very very very strong</strong>
</div>
span
em
strong
都是行內(nèi)元素野崇,背景顏色標(biāo)出了它們行內(nèi)框所占的空間(這里只是為了圖示方便,用顏色標(biāo)示了行內(nèi)框占據(jù)的大致空間亩钟,因?yàn)?css 中并沒有給行內(nèi)元素的行內(nèi)框設(shè)置顏色的屬性)乓梨。而如果沒有用行內(nèi)元素包裹的文字呢?瀏覽器會(huì)給文字生成一個(gè)匿名的行內(nèi)框清酥,如下圖ih-pic3.png 中用綠色框框出來的所示扶镀。
<div style="border:1px solid black; color:gray;">
anonymous <span>span</span> <em>em</em>
</div>
上面兩個(gè)例子字體都是統(tǒng)一大小的,如果每個(gè)行內(nèi)元素字體不一樣大小呢焰轻?每個(gè)行內(nèi)框會(huì)根據(jù)各自的 vertical-algin
在垂直方向上對齊臭觉,我們先看下示例(示例中每個(gè)行內(nèi)元素的 vertical-algin
都設(shè)置成一樣,但是每個(gè)元素的 vertical-algin
也可以各自不同,這里我就省略了各自不同的例子):
<style>
span { font-size: 32px; }
em { font-size: 20px; }
strong { font-size: 16px; }
.vertical-align-wrapper * {
vertical-align: top; /* 對應(yīng)的是圖ih-pic5.png */
/* vertical-align: middle; 對應(yīng)的是圖ih-pic6.png*/
/* vertical-align: baseline; 對應(yīng)的是圖ih-pic7.png*/
/* vertical-align: bottom; 對應(yīng)的是圖ih-pic8.png*/
}
</style>
<div class="vertical-align-wrapper" style="border:1px solid black; color:gray;">
anonymous <span>span</span> <em>em</em> <strong>I am very very very very very very strong</strong>
</div>
如果一行中的行內(nèi)元素字體大小和 verticial-align
各不相同蝠筑,那么整個(gè)一行占據(jù)空間是如何算的呢忆肾?這一行的高度最高處由垂直方向上行內(nèi)框最高的元素決定,最低處由垂直方向上行內(nèi)框最低的元素決定(代碼菱肖,圖如下):
<style>
span { font-size: 30px; vertical-align: top; }
em { font-size: 30px; vertical-align: top; }
strong { font-size: 30px; vertical-align: top; }
</style>
<div style="border:1px solid black; color:gray; vertical-align: bottom;">
anonymous <span>span</span> <em>em</em> <strong>I am very very strong</strong>
</div>
圖ih-pic9.png 中黃色框表示的就是整行占據(jù)的空間客冈,我們稱之為行框(line-box)。再給一個(gè)帶上下標(biāo)的示例看看是如何計(jì)算行框占據(jù)的空間(還是黃色框占據(jù)的空間表示行框):
<div style="border:1px solid black; color:gray; vertical-align: bottom;">
anonymous <span>span</span><sup>sup</sup> <em>em</em><sub>sub</sub> <strong>I am very very strong</strong>
</div>
所以行內(nèi)元素字體大小決定了字的大小稳强,每個(gè)行內(nèi)元素占據(jù)的空間又由行內(nèi)框(inline-box)決定场仲,最后一整行如果只有行內(nèi)元素,則所占據(jù)的空間就是行框(line-box)決定的退疫,高度亦是行框的高度渠缕。如果文字很長折成了多行,那么每行都是按照以上規(guī)則生成多個(gè)行框褒繁,并在垂直方向上以容器內(nèi)部邊界的左上角開始向下排列:
接下來我們就要看下 line-height 是怎么影響元素的了亦鳞。剛剛我們提到一個(gè)行內(nèi)元素占據(jù)的空間叫行內(nèi)框,那么行內(nèi)框的高度是如何決定的呢棒坏?就是 css 屬性行高(line-height
)決定燕差,瀏覽器通常全局的默認(rèn)行高是 normal,約等于字體的1.2倍坝冕。譬如一個(gè)行內(nèi)元素的字體是 12px徒探,那么行高就是 12 x 1.2 = 14.4(px)。而行高和字體間的距離差距我們叫做 leading(中文可以叫做行間距喂窟,不過網(wǎng)頁上的行間距和傳統(tǒng)印刷業(yè)的行間距代表的地方不同测暗,這里就不描述其歷史和不一樣的地方了)。瀏覽器會(huì)把 leading 除以二磨澡,平均放到字體的上面和下面碗啄,這兩塊平均空間的距離叫做 half-leading,如下圖(圖中為了演示明顯稳摄,字體大小設(shè)為 16px稚字,行高設(shè)為 32px):
如果 line-height 小于字體大小,那么行內(nèi)框的高度就會(huì)向字體的中心線收縮秩命,字體將會(huì)溢出行高尉共,如下圖(圖中為了演示明顯褒傅,字體大小設(shè)為 32px弃锐,行高設(shè)為 20px):
行內(nèi)框的高度會(huì)影響行框的高度,我們假設(shè)極端情況每行只有一個(gè)行內(nèi)元素殿托,那么每行行框(line-box)的高度就是這個(gè)元素行內(nèi)框(inline-box)的高度霹菊,讓我們看看多行情況下行高(line-height)大于字體和小于字體產(chǎn)生的效果,溢出行高的字體將在多行垂直方向疊加:
最后我們說下如果手工設(shè)置了元素的 line-height,將如何影響元素:
- 行內(nèi)元素設(shè)置 line-height 等于指定了行內(nèi)框的高度旋廷。
- 沒有行內(nèi)元素包裹的文字所生成的匿名行內(nèi)框的高度是繼承自它的父容器鸠按。
- 塊級(jí)元素包裹了一系列行內(nèi)元素,這些行內(nèi)元素自己沒有設(shè)置 line-height饶碘,那么給塊級(jí)元素設(shè)置 line-height 等于給被包裹的行內(nèi)元素指定了最小行內(nèi)框的高度目尖。
現(xiàn)象產(chǎn)生原因分析
一般情況:
<div style="height:32px; line-height:32px; border:1px solid black;">
<span>Test<span>
</div>
div 設(shè)置的 line-height:32px 給 span 限定了行內(nèi)框的最小高度,而 span 的默認(rèn)行高和字體都沒有超過 32px扎运,所以 span 的行內(nèi)框高度就是 32px瑟曲,又因?yàn)?half-leading 平均分配的原理,那么字體就會(huì)在行內(nèi)框中居中豪治,如果一行字行內(nèi)框高度相同洞拨,那么整行文字的行框高度就是 32px,最后 div 的高度又設(shè)置了 32px负拟,所以最后文字看起來是垂直居中的烦衣。(其實(shí)如果以此例來說 div 不設(shè)置高度也沒有問題,因?yàn)?div 高度就是按內(nèi)部元素高度而撐開的)
異常情況:
<div style="height:32px; line-height:32px; border:1px solid black; font-size:42px">
<span style="font-size:20px;">Test<span>
</div>
雖然看上去 div 只有一個(gè)子元素 span掩浙,但是從規(guī)范上我得知花吟,這種情況下有一個(gè)寬度無限小的匿名行內(nèi)框存在。如果在 div 上設(shè)置了字體 42px厨姚,那么這個(gè)匿名行內(nèi)框的字體就變成了 42px示辈,且 div 的 line-height 只能指定匿名行內(nèi)框的最小高度,當(dāng)匿名行內(nèi)框的字體大于 32px 時(shí)遣蚀,這個(gè)匿名行內(nèi)框的高度就變大超過了 32px矾麻,最后導(dǎo)致整行的行框高度超過 32px。而 span 的行內(nèi)框和字體雖然都沒有超過 32px 芭梯,但是它的 baseline 需要在水平方下沉和這個(gè)匿名行內(nèi)框?qū)R险耀,但是 div 的高度限制成了 32px,最后看上去 span 就不在垂直居中的方向上了玖喘。