深入理解行內(nèi)元素的布局

前言

總括: 本文通過(guò)實(shí)例講解CSS中最大的難點(diǎn)之一,行內(nèi)元素的布局,主要是挖掘line-height和vertical-align兩個(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">
  ![](http://upload-images.jianshu.io/upload_images/1394123-beaa89c3197bcd45?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)<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 textfollowed by more inline text.和一個(gè)span行內(nèi)盒。

OK,至于怎么觸發(fā)塊級(jí)格式上下文請(qǐng)看塊格式化上下文。這里只為了說(shuō)明IFC而介紹下BFC。

當(dāng)元素的 CSS 屬性 display 的計(jì)算值為 inline, inline-blockinline-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'>
    ![](http://upload-images.jianshu.io/upload_images/1394123-beaa89c3197bcd45?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)<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;
}

實(shí)例

<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è)例子:

下面沒(méi)內(nèi)容戳這

<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è)??:

下面沒(méi)示例內(nèi)容請(qǐng)戳這

<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)呢....???♀?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 問(wèn)答題47 /72 常見(jiàn)瀏覽器兼容性問(wèn)題與解決方案? 參考答案 (1)瀏覽器兼容問(wèn)題一:不同瀏覽器的標(biāo)簽?zāi)J(rèn)的外補(bǔ)...
    _Yfling閱讀 14,172評(píng)論 1 92
  • 有些東西我們經(jīng)常用,但是我們卻并不了解它的原理,所以一旦換了場(chǎng)景,好多東西就不知道該怎么用了。最近一直很糾結(jié)ver...
    朱小維閱讀 5,235評(píng)論 8 34
  • 參考文章:深入了解CSS的line-height屬性Vertical-Align: 你需要知道的所有事【譯】Ver...
    若邪Y閱讀 3,568評(píng)論 1 6
  • 以下文章是我在網(wǎng)上收集的內(nèi)容,為了記錄自己的學(xué)習(xí)以及為了以后不到處找而記錄下來(lái),如果對(duì)你有用,請(qǐng)感謝寫(xiě)這些文章的前...
    DCbryant閱讀 1,031評(píng)論 0 2
  • 這么多天不更新是因?yàn)橐Y(jié)婚忙的事情太多 一向淡定的林大頭知道今天要坐車(chē)回去結(jié)婚后也變得不淡定了,昨晚一直處于亢奮狀...
    林大頭家的小兔子閱讀 844評(píng)論 0 2

友情鏈接更多精彩內(nèi)容