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