前言
總括: 本文通過(guò)實(shí)例講解CSS中最大的難點(diǎn)之一,行內(nèi)元素的布局,主要是挖掘line-height和vertical-align兩個(gè)屬性在布局方面的使用。
原文博客地址:深入理解行內(nèi)元素的布局
知乎專(zhuān)欄&&簡(jiǎn)書(shū)專(zhuān)題:前端進(jìn)擊者(知乎)&&前端進(jìn)擊者(簡(jiǎn)書(shū))
博主博客地址:Damonare的個(gè)人博客
白茶清歡無(wú)別事,我在等風(fēng),也在等你。??
正文
講道理line-height和vertical-align 這對(duì)基是十分低調(diào)的,日常開(kāi)發(fā)中碰到的很多莫名其妙的bug很大一部分都是這倆貨搞出來(lái)了的,但很少有人知道這對(duì)基的罪惡,因?yàn)榭赡芑ㄊ礁膶?xiě)一下CSS代碼問(wèn)題就解決了。實(shí)際上搞明白這倆東西才能讓我們?cè)诓季止ぷ髦杏稳杏杏唷1疚慕酉聛?lái)就通過(guò)這對(duì)基的關(guān)系來(lái)了解內(nèi)聯(lián)元素具體的布局問(wèn)題~we are刨根問(wèn)底攔不住~??
? 讀這篇文章之前請(qǐng)確定您有以下知識(shí)基礎(chǔ):
- line-height的數(shù)字值是和font-size大小相關(guān)的;
- vertical-align的百分比值是和line-height值相關(guān)的;
引出vertical-align
首先來(lái)看一個(gè)??:
<div class="test">
<span>Xx</span>
</div>
.test {
background: red;
}
img {
width: 50px;
height: 50px;
}
span {
background: white;
}
下面無(wú)實(shí)例內(nèi)容的話(huà)戳這里
<script async src="https://jsfiddle.net/Damonare/5oLvd0z4/embed/html,css,result/"></script>
? 如上代碼片段result所示,圖片下面有一明顯的紅色條條,??什么鬼,很詭異不是么,我要的是img充滿(mǎn)整個(gè)div啊!!!好吧,我加了一個(gè)額外的inline元素填寫(xiě)內(nèi)容Xx,發(fā)現(xiàn)原來(lái)多出來(lái)的那一塊正好是文字的下半空白部分。吆喝,這么巧?實(shí)際上,如果將這里的Xx內(nèi)容去掉,只剩下img,那個(gè)條條依然存在,表現(xiàn)行為好像父元素div里面除了img元素還有一個(gè)空白的元素一樣,??姑且叫它空白節(jié)點(diǎn)吧(肉眼中不存在卻在影響著布局),這個(gè)是比較詭異的一個(gè)表現(xiàn),查標(biāo)準(zhǔn)沒(méi)找到有相關(guān)的說(shuō)明。但請(qǐng)將這個(gè)空白節(jié)點(diǎn)先記住,我們的重點(diǎn)是研究條條是咋出來(lái)的。這條條看上去貌似是文本和圖片垂直方向上對(duì)齊生成的,那么這就引粗來(lái)一個(gè)問(wèn)題,inline元素默認(rèn)的垂直方向的對(duì)齊方式是什么樣的?也就是vertical-align的默認(rèn)值是啥?
? OK,??I know you know。vertical-align默認(rèn)值是baseline,OK,那就先來(lái)挖一挖vertical-align具體是個(gè)什么鬼。
Vertical-align(1)
vertical-align這個(gè)屬性我感覺(jué)是CSS中最復(fù)雜的屬性之一了...好多問(wèn)題概念也讓人看不懂...一方面它是作用在inline元素和table-cell元素身上,屬性值特別多,另一方面該屬性規(guī)范里并沒(méi)有一個(gè)定論,導(dǎo)致一些屬性不同瀏覽器的實(shí)現(xiàn)也不同,所以兼容性問(wèn)題很多。對(duì)于一些 可替換元素,比如textarea, HTML標(biāo)準(zhǔn)沒(méi)有說(shuō)明它的基線(xiàn),這意味著對(duì)其使用這個(gè)關(guān)鍵字,各瀏覽器表現(xiàn)可能不一樣。我們這里先研究一下它的默認(rèn)值baseline。
baseline字面意思就是基線(xiàn),何為基線(xiàn)?首先請(qǐng)記住下面這幾個(gè)概念:
- 基線(xiàn):小寫(xiě)字母'x'的下邊緣所在的那條線(xiàn);
- x-height: 小寫(xiě)字母'x'的高度;
- ex: 1ex就是一個(gè)小寫(xiě)字母'x'的高度,類(lèi)似em單位,注意,ex和em都是相對(duì)單位;
我們看下CSS標(biāo)準(zhǔn)里怎么說(shuō)的:相關(guān)標(biāo)準(zhǔn)鏈接
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'元素的基線(xiàn)是標(biāo)準(zhǔn)流中最后一個(gè)line box(行盒)的基線(xiàn), 除非這個(gè)line box里面既沒(méi)有in-flow line boxes(行內(nèi)框)或者本身’overflow’屬性的計(jì)算值不是’visible’, 這種情況下基線(xiàn)是該元素margin底邊緣。
那么上面現(xiàn)象就很容易解釋的通了,我們知道img元素默認(rèn)的表現(xiàn)形式和inline-block元素一樣,它的基線(xiàn)就是margin底邊緣,而inline元素本身是有高度的,兩者基線(xiàn)對(duì)齊自然就如上面那樣表現(xiàn)了。??
??好吧,等會(huì),到這里,我們發(fā)現(xiàn)實(shí)際又多了倆概念——inline元素的高度問(wèn)題和標(biāo)準(zhǔn)里說(shuō)的line box(IFC)。
首先我們先來(lái)看下inline元素的高度問(wèn)題,即——line-height屬性。
Line-height
CSS中起高度作用的只有height和line-height兩個(gè)屬性吧。如果一個(gè)元素沒(méi)設(shè)置height那么其最終的高度一定是由line-height決定的。之前inline元素的高度我以為是文字內(nèi)容撐開(kāi)的,但實(shí)際研究了下并不是這樣的,看下面的??:
.demo1{
font-size: 20px;
line-height: 0;
border: 1px solid blue;
background: red;
}
.demo2{
font-size: 0;
line-height: 20px;
border: 1px solid red;
background: yellow;
}
HTML代碼:
<div class="demo1">測(cè)試</div>
<div class="demo2">測(cè)試</div>
下面沒(méi)內(nèi)容戳這或者自行拷貝代碼本地測(cè)試
<script async src="https://jsfiddle.net/Damonare/54ucnkht/embed/html,css,result/"></script>
如上可證明,inline元素的高度決定者是line-height,并不是文字內(nèi)容撐開(kāi)的。??
CSS規(guī)范里對(duì)line-height的默認(rèn)值有這么一句話(huà):
We recommend a used value for 'normal' between 1.0 to 1.2.
只是推薦...??是不是說(shuō)實(shí)際上各個(gè)瀏覽器對(duì)于line-height的默認(rèn)值實(shí)現(xiàn)不一定是一樣的,但都介于1.0-1.2之間。具體各大瀏覽器的實(shí)現(xiàn)值待查證。這里需要記住line-height的默認(rèn)值是啥就OK。
IFC
在之前的博文CSS的盒子模型里面,有拓展過(guò)相關(guān)知識(shí),簡(jiǎn)短的介紹了下BFC和IFC,相較于BFC,IFC要復(fù)雜得多,規(guī)范里IFC的篇幅也要比BFC多得多。
簡(jiǎn)要總結(jié)下BFC,即塊級(jí)元素可能會(huì)觸發(fā)塊級(jí)格式上下文(BFC),在塊級(jí)格式上下文中,塊級(jí)盒子豎直方向排列,不受上下文外部元素影響,自成一方世界。塊容器盒指的是那些包含元素的盒子,塊容器盒可能包含其它塊級(jí)盒,也可能生成一個(gè)行內(nèi)格式上下文(IFC)。??
但塊容器盒要么只包含行內(nèi)盒,要么只包含塊級(jí)盒。但通常會(huì)同時(shí)包含兩者。在這種情況下,將創(chuàng)建匿名塊盒來(lái)包含毗鄰的行內(nèi)級(jí)盒。
看個(gè)??:
//demo1
<div>
Some inline text
<p>followed by a paragraph</p>
followed by more inline text.
</div>
// demo2
<p>
Some inline text
<span>followed by a paragraph</span>
followed by more inline text.
</p>
如上,demo1將創(chuàng)建兩個(gè)匿名塊盒,一個(gè)包含 p前面的文本 (Some inline text), 一個(gè)包含 p 后面的文本(followed by more inline text)。
demo2將生成一個(gè)行內(nèi)格式上下文,生成一個(gè)匿名行盒(line box),里面包含兩個(gè)匿名行內(nèi)盒(inline box),Some inline text和followed by more inline text.和一個(gè)span行內(nèi)盒。
OK,至于怎么觸發(fā)塊級(jí)格式上下文請(qǐng)看塊格式化上下文。這里只為了說(shuō)明IFC而介紹下BFC。
當(dāng)元素的 CSS 屬性
display的計(jì)算值為inline,inline-block或inline-table時(shí),稱(chēng)它為行內(nèi)級(jí)元素。行內(nèi)級(jí)元素生成行內(nèi)級(jí)盒(inline-level boxes),參與行內(nèi)格式化上下文(inline formatting context)。同時(shí)參與生成行內(nèi)格式化上下文的行內(nèi)級(jí)盒稱(chēng)為行內(nèi)盒(Inline boxes)。
規(guī)范里IFC文字很多,提煉下我們需要的:
如果一個(gè)矩形區(qū)域,包含著一些排成一條線(xiàn)的盒子,稱(chēng)為line box。
一個(gè)line box的寬度,由他的包含塊(containg block)和floats的存在情況決定。line box的高度,由你給出的代碼決定。(line-height),即所有inline box的最大高度差。
當(dāng)盒子的高度小于父級(jí)盒子高度時(shí),垂直方向的對(duì)齊'vertical-align'屬性決定。
Vertical-align(2)
在上面的vertical-align(1)中主要了解了什么是baseline,以及它是如何確定的。我們繼續(xù)研究這個(gè)屬性,看下面說(shuō)明表格:
| 值 | 描述 |
|---|---|
| baseline | 默認(rèn)。元素放置在父元素的基線(xiàn)上。 |
| top | 把元素的頂端與行中最高元素的頂端對(duì)齊 |
| text-top | 把元素的頂端與父元素字體的頂端對(duì)齊 |
| middle | 把此元素放置在父元素的中部。 |
| bottom | 把元素的頂端與行中最低的元素的頂端對(duì)齊。 |
| text-bottom | 把元素的底端與父元素字體的底端對(duì)齊。 |
除了baseline我們已經(jīng)很了解之外,其它幾個(gè)屬性我們貌似也能看懂,唯一的問(wèn)題可能是父元素的頂端低端都是什么鬼???需要確定一下,好的再次拿我們第一個(gè)例子來(lái)講解,但我們需要變一下,加點(diǎn)東西進(jìn)去:
<div class="demo">
<span class='line-box'>
<span>Xx</span>
</span>
</div>
.demo {
background: red;
}
.line-box {
background: blue;
line-height: 200px;
}
.line-box img {
vertical-align: text-bottom;
width: 50px;
}
.line-box span {
margin-left: 20px;
color: yellow;
}
<script async src="https://jsfiddle.net/Damonare/ck07neus/embed/html,css,result/"></script>
通過(guò)IFC部分我們知道,之前的例子實(shí)際上有生成一個(gè)匿名行盒(line box),雖然他可以繼承父元素的屬性,但我們沒(méi)法直觀(guān)的去操作它??,OK,把這個(gè)匿名行盒變成可控的span元素就好了??,如上demo所示。
我們通過(guò)設(shè)置line box的line-height來(lái)控制line-box的高度,然后設(shè)置img的vertical-align屬性值,來(lái)觀(guān)察具體的對(duì)齊方式。OK,讀者你可以自行本地測(cè)試或是直接更改fiddle內(nèi)容來(lái)看效果。但這里很容易有個(gè)誤區(qū),就是父元素的middle,top這些值是怎么確定的?如上,我們通過(guò)更改img元素的vertical-align的值,來(lái)觀(guān)察區(qū)別,表面上看著好像是父元素根據(jù)Xx內(nèi)容來(lái)進(jìn)行確定的,實(shí)則不然。我們?cè)賮?lái)看一個(gè)例子:
<script async src="http://jsfiddle.net/Damonare/gkqq3dvp/embed/html,css,result/"></script>
上面例子中,我們更改了Xx的對(duì)齊方式,發(fā)現(xiàn)了很奇特的現(xiàn)象??,當(dāng)Xx設(shè)置為text-bottom或是text-top的時(shí)候父元素(ling box)被撐大了??,但這另一方面這也證明了,父元素的基線(xiàn)和中線(xiàn)等并不是由文本Xx決定的,誰(shuí)決定的呢?前面提過(guò)的那個(gè)空白節(jié)點(diǎn)決定的!??這個(gè)空白節(jié)點(diǎn)實(shí)際上是理解內(nèi)聯(lián)元素布局的重點(diǎn)!不知道它的存在,很多問(wèn)題是搞不清楚的。那么這個(gè)空白節(jié)點(diǎn)又到底是怎么影響布局的呢?前面說(shuō)過(guò)基線(xiàn)的決定著是小寫(xiě)字母x,這個(gè)時(shí)候問(wèn)題就來(lái)了,可能你早就想問(wèn)了,不同字體下面的小寫(xiě)字母x底部邊緣肯定是有區(qū)別的啊,好,我們?cè)谘芯肯?code>font-family。
Font-family
我們?cè)賮?lái)看一個(gè)??:
<script async src="http://jsfiddle.net/Damonare/kyse4v44/embed/html,css,result/"></script>
關(guān)于字體具體的知識(shí)可以看這篇博文,我簡(jiǎn)單的總結(jié)一下重點(diǎn)。首先字體是有一個(gè)字體度量的概念的,
- 一款字體會(huì)定義一個(gè) em-square,它是用來(lái)盛放字符的金屬容器。這個(gè) em-square 一般被設(shè)定為寬高均為 1000 相對(duì)單位,不過(guò)也可以是 1024、2048 相對(duì)單位。
- 字體度量都是基于這個(gè)相對(duì)單位設(shè)置的,包括 ascender、descender、capital height、x-height 等。注意這里面的值是可以超出em-square范圍的。
- 在瀏覽器中,上面的 1000 相對(duì)單位會(huì)按照你需要的 font-size 縮放。
看上面的例子我們也能看出來(lái),實(shí)際上一個(gè)內(nèi)聯(lián)元素是有兩個(gè)高度的content-area高度(background-color實(shí)際渲染的那個(gè)高度)和 virtual-area 高度(實(shí)際區(qū)域占空間的高度也就是line-height)。??
結(jié)論
- 所有的內(nèi)聯(lián)元素都有兩個(gè)高度
- 基于字體度量的 content-area
- virtual-area(也就是 line-height )
- 內(nèi)聯(lián)元素都有一個(gè)
空白節(jié)點(diǎn)存在著來(lái)確定基線(xiàn)等概念; - 基線(xiàn)的確定和字體有關(guān),和內(nèi)部的內(nèi)聯(lián)元素?zé)o關(guān);
- IFC很難懂;
- line-box(行盒) 的高度的受其子元素的 line-height 和 vertical-align 的影響;
- 我們貌似沒(méi)法用CSS來(lái)更改字體度量。
題目確實(shí)有些標(biāo)題黨的嫌疑了,實(shí)際上也沒(méi)有挖很深,比如vertical-align在inline-table元素的作用效果以及sup,sub等其他的屬性值,以及l(fā)ine-height具體的屬性值如何生效的都沒(méi)有涉及。我想把這篇文章重點(diǎn)放在布局上,而且篇幅也有限。沒(méi)涉及的請(qǐng)自行查閱資料吧,在此說(shuō)聲抱歉。??
??以上。
后記
? 從剛開(kāi)始做前端,身邊CSS簡(jiǎn)單易學(xué)但很坑的聲音就不絕于耳,很多人也說(shuō)HTML、CSS一星期就能學(xué)會(huì),現(xiàn)在漸漸覺(jué)得真是謬論。是,單純掌握浮動(dòng),定位,對(duì)齊,居中等基礎(chǔ)能解決一大半的布局問(wèn)題,甚至百分之百,因?yàn)楹芏嗲闆r真的是變個(gè)寫(xiě)法莫名其妙就實(shí)現(xiàn)了想要的結(jié)果??赡苓@也是很多人說(shuō)CSS坑的原因,但實(shí)際上很多開(kāi)發(fā)者是不看CSS標(biāo)準(zhǔn)的,模仿個(gè)網(wǎng)站或是看著視頻寫(xiě)個(gè)demo就覺(jué)得掌握了CSS,遠(yuǎn)遠(yuǎn)不是這樣的。漸漸覺(jué)得深挖CSS要比深挖JavaScript難的多...你覺(jué)得CSS坑?誰(shuí)讓你不看標(biāo)準(zhǔn)呢....???♀?