w3c係列之CSS(四):深入理解盒模型
盒模型是一個老生常談的話題,本身並不複雜,它描述了文檔樹所產生的矩形框以及根據可視化格式模型(visual formatting model)所產生的布局結構。
曾經看到一道麵試題:說說CSS中的盒模型?網上的回答都是margin,border,padding,width,height之類的,尼瑪都是複製粘貼的吧= =.....要是這篇文章也這麼寫的話就跟前麵三篇沒啥區別了,既然要深入理解,那就談點高大上的東西。
1.要深入,先入門
首先還是得對盒模型做個簡單而詳細的介紹,想要跑,基礎要打好~,盜用w3c一張圖:

我們從裏到外逐步分析:
- 最裏麵是Content,決定Content大小的因素有很多,比如width,height屬性,content內部是否還有box,是否是table等,所有的這些都將在可視化模型中詳細講到;
- 然後是padding內邊距,這個沒啥好講的;
- 再外麵是border,這個比較好玩~,雖然內外邊距和邊框都能單獨設置四個方向的尺寸,但是CSS為border提供了更多的設置:border-color,border-style,有興趣可以去看另外一篇寫border的文章:CSS好玩的邊框。
- 最外麵是margin(注意他是透明的)。一個盒子沒啥講的,許多盒子在一起就有意思了。有可能會出現collapsing margins(外邊距坍塌),也就是相鄰的兩個盒子的垂直外邊距會合並(水平外邊距是不會合並的)。
這就是盒模型的簡單介紹了。不過盒模型可沒這麼簡單,並不是所有元素都支持所有的margin和padding(比如內聯元素,設置豎直方向的margin,padding是無效的),而且,雖然前麵說了兩個盒子豎直排列會發生垂直外邊距坍塌,但並不是所有時候都會這樣,比如下麵這種情況:
<style> body{background:#aaa} div{ margin:50px; background:#0ff; width:50%;height:50px; float:left; } </style> <body> <div> box1 </div> <div> box2 </div> </body>

我擦,那到底什麼時候會合並,什麼時候才不會合並啊?以前看書的時候沒說過這種情況啊!/*那是因為你沒有去看w3c*/折疊也是有規則的:
1. 兩個或多個毗鄰的普通流中的塊元素垂直方向上的margin會折疊
2. 浮動元素/inline-block的元素/絕對定位元素的margin不會和垂直方向上的其他元素的margin折疊
3. 創建了塊級格式化內容(傳說中的BFC)的元素,不和它的子元素發生margin折疊
4. 元素自身的margin-bottom和margin-top相鄰時也會折疊
2. 浮動元素/inline-block的元素/絕對定位元素的margin不會和垂直方向上的其他元素的margin折疊
3. 創建了塊級格式化內容(傳說中的BFC)的元素,不和它的子元素發生margin折疊
4. 元素自身的margin-bottom和margin-top相鄰時也會折疊
更加詳細的折疊計算戳這裏:https://bbs.csdn.net/topics/340188875,下麵直接進入本文重點。
2.可視化格式模型(Visual formatting model)
2.1簡介
這中文名字聽起來挺別扭的,其實就是瀏覽器如何將document tree處理成你所看到的頁麵的過程模型。
/*為了方便以後就叫VFM吧...*/
不過VFM也不是描述了頁麵處理的全部細節,比如它並未描述letter-spacing的格式算法。(又是一個坑)
在VFM中,每一個DOM元素都會創建0個或多個盒子,這些盒子的布局通常受下麵一些因素的影響:
- 盒子的尺寸和類型
- 盒子的定位方式(普通流,浮動流,絕對定位)
- document tree中元素之間的關係
- 額外因素(比如viewport的大小,圖片的固有尺寸等)
viewport是啥?就是你屏幕上所看到的頁麵區域啦,通常頁麵區域比viewport大,所以需要一個滾動條來查看下麵的內容,就像這篇文章一樣。(當然你可以用鼠標滾輪)
2.2盒子的類型
盒模型不隻是padding,border,margin,他還有許多不同的類型。這通常取決於盒子的display屬性。
2.2.1塊級元素(block-level elements )和塊框(block boxes)
如果元素的display屬性為block,list-item或者table,那麼該元素就會成為block-level元素。這裏有幾個概念要搞清:
- containing box:包含框。一個元素的包含框就是包含它的框,比如<div><p>test</p></div>,p的包含框就是div,而div為p建造了一個包含框。
- block-level box:塊級框,塊級元素所產生的框。塊級元素(不包含 table),會形成僅包含塊框或僅包含行內框的主塊框( principal block box )。主塊框會為子孫元素建立包含框,生成內容,並且也是涉及所有定位體係的框。主塊框參與BFC。
- block container box:塊容器框。塊容器框要麼隻包含塊級框,要麼隻包含內聯框(inline-level boxes)。雖然名字有“塊”,但並不是所有塊容器框都是塊級框:non-replaced inline blocks,non-replaced table cells都是塊容器框,但不是塊級框。
- block box:塊框,既是塊級框,也是塊容器框的框。
有些時候,在上下文很明確的條件下,"block-level box", "block container box" 和"block box"都縮寫為"block"。
是不是有點暈了= =,慢慢看就理解了,為了避免翻譯問題,後麵遇到專業術語都用英文表示吧......
這節術語真是多,又出現了一個anonymous block boxes(匿名塊框),比如像下麵這種情況:
<div> Some text <p>More text</p> </div>假設div,p都是塊級框(display:block),div中有內聯文本Some text和塊級文本More text,但是為了更好地生成格式化模型,會給內聯文本外麵加一個匿名塊框。
https://www.w3.org/TR/2011/REC-CSS2-20110607/images/anon-block.png
換句話說:如果一個block container box包含了一個block-level box,那麼標準會強製使裏麵隻包含block-level的boxes(或者run-in boxes)。
2.2.2內聯元素(inline-level elements)和內聯框(inline boxes)
內聯元素就是那些不會生成新的內容塊的元素,這些內容都是分布在同一行的。當display屬性為inline,inline-table,inline-block時就會生成inline-level boxes,這些boxes會參與IFC。inline box就是內容參與其內部包含的IFC的inline level box。display為inline的non-replaced element會生成inline box。但是inline-level
boxes並等於inline boxes(比如inline-table elements,inline-block elements,replaced inline-level elements)。那些不屬於inline boxes的inline-level boxes被稱作atomic inline-level boxes,這些boxes作為單獨的不透明的框參與IFC。
有匿名塊框就有匿名內聯框(anonymous inline boxes),比如像下麵這種情況:
<pre name="code" ><p>some <em>name</em> </p>some就是匿名內聯框。可以看到與匿名塊框不同的是,塊級框p裏麵包含的不是塊級框了,而是內聯框em,所以會產生匿名的some內聯框。之所以叫他們匿名框,是因為沒有相應的內聯元素與之對應,但是他們的屬性還是可以從p那裏繼承到的。
可以看到em的後麵還有很多空格,根據“white-space”的特性這些空格會被壓縮,所以這裏不會產生匿名內聯框。
匿名塊框和匿名行內框統稱為匿名框。
2.2.3 display屬性
可取值: inline | block | list-item | run-in | compact | marker |table | inline-table | table-row-group | table-header-group |table-footer-group
| table-row | table-column-group | table-column |table-cell | table-caption | none | inherit初始值:
inline適用於: 所有元素可否繼承:
否百分比: N/A媒介:
所有
display:run-in是CSS3中新添加的屬性,添加了該屬性的元素會根據上下文機智地作為塊級元素或內聯元素顯示。(怎麼個機智法?真坑...)
注意,盡管display初始值為inline,但是UA的初始值可能會覆蓋它,比如div,p的display值默認為block。各個元素的默認值,在HTML4中就有告知:
2.3定位機製
CSS中有三大定位體係:normal flow(普通流),floats(浮動流),absolute positioning(絕對定位)。定位方式與postion屬性有關:
可取值:
static | relative | absolute | fixed | inherit初始值:
static適用於: all elements可否繼承:
no百分比: N/A媒體:
visual計算值: as specified
其中,static,relative屬於常規流,absolute和fixed屬於絕對定位,如果元素的float屬性不為none,那麼他就屬於浮動流。
絕對定位所謂的絕對其實是相對的,absolute元素是相對其第一個具有relative特性的祖先元素偏移的,如果祖先元素都是static的,那麼就相對根元素偏移(UA一般會將根元素設為static)。而fixed是相對於屏幕內容區域定位的,比如有的網站導航欄時鍾顯示在網頁上方,這就是用了fixed定位。
另外,絕對定位是脫離文檔流的,他們的位置不會對普通流的定位產生任何影響。
常規流(普通流)中的boxes都屬於formatting context(格式化上下文)。/*格式化上下文這翻譯真心讓人難以理解,context有環境之意,這句話的意思應該就是說常規流中的盒子定位時們都會受到其他盒子的影響*/之前有說到BFC和IFC,BFC就是block formatting contexts,IFC就是inline
formatting contexts,看名字就知道是常規流中的一種。relative positioning也是常規流中的一種。
有關這三大定位的單獨介紹網上有很多詳細的資料,這裏不一一展開,這裏對這三者進行一個比較。
為了說明三者間差異,我們舉個例子:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <HTML> <HEAD> <TITLE>Comparison of positioning schemes</TITLE> </HEAD> <BODY> <P>
Beginning of body contents. <SPAN >
Start of outer contents. <SPAN >
Inner contents.
</SPAN> End of outer contents.
</SPAN> End of body contents. </P> </BODY> </HTML>我們為它添上如下樣式:
body { display: block; font-size:12px; line-height: 200%;width: 400px; height: 400px } p { display: block } span { display: inline }首先來看普通流,為了區分我們給文字加上顏色:
#outer { color: red } #inner { color: blue }https://www.w3.org/TR/2011/REC-CSS2-20110607/images/flow-generic.png
上麵說到了,p中隻有內聯元素span,所以會產生匿名內聯框,p中所有的內容都會作為IFC呈現。
再看一下相對定位的情況,我們更改#outer和#inner的屬性:
#outer { position: relative; top: -12px; color: red } #inner { position: relative; top: 12px; color: blue }https://www.w3.org/TR/2011/REC-CSS2-20110607/images/flow-relative.png
可以看到#outer和#inner都相對於原來位置產生了偏移。
然後再看看float定位是什麼效果,繼續更改#outer和#inner屬性:
https://www.w3.org/TR/2011/REC-CSS2-20110607/images/flow-float.png
最後看下絕對定位的效果:
#outer { position: absolute; top: 200px; left: 200px; width: 200px; color: red; } #inner { color: blue }https://www.w3.org/TR/2011/REC-CSS2-20110607/images/flow-absolute.png
絕對定位和relative一起用:
#outer { position: relative; color: red } #inner { position: absolute; top: 200px; left: -100px; height: 130px; width: 130px; color: blue; }
效果如下:
https://www.w3.org/TR/2011/REC-CSS2-20110607/images/flow-abs-rel.png
如果不把#outer設為相對定位,就會像下麵這樣:
https://www.w3.org/TR/2011/REC-CSS2-20110607/images/flow-static.png
如果你能理解上麵這些代碼產生的效果,那麼你就理解了三者之間的差異。
2.4 position,display,float屬性之間的關係
這三個屬性與盒模型定位息息相關。三者之間的相互影響遵循下麵的規則:
- 如果display為none,那麼position和float失效。因為這種情況下不產生框(注意是不產生框!如果用visibility:hidden或者opacity:0的話那叫隱藏框,雖然你看不見框,但框就在那裏...);
- 否則,如果display為absolute或fixed,即絕對定位,這時float會失效;
- 否則,如果float不為none,那麼display屬性會發生相應的改變,具體見下表;
- 否則,如果元素是根元素,那麼display也會按下表相應變化;
- 如果以上情況都不滿足,就用你在CSS裏指定的那個值啦~
Specified value(指定的值) | Computed value(計算後的值) |
---|---|
inline-table | table |
inline, table-row-group, table-column, table-column-group, table-header-group, table-footer-group, table-row, table-cell, table-caption, inline-block | block |
others | same as specified |
其實有關定位並不僅僅隻涉及到這三個屬性,還有z-index也用的比較多。(有關z-index的深入介紹,戳這裏:https://readit.sinaapp.com/?p=55)
3.總結
這篇坑爹的文章涉及的概念很多,不理解去網上查,或者直接去w3c:https://www.w3.org/TR/2011/REC-CSS2-20110607/visuren.html。如果文中有錯誤請及時指出!一切後果本人概不負責!嗬嗬!
本文算是對盒模型做了一個比較深入的介紹,但是對visual formatting model卻隻是作了一個簡單的介紹,後麵會對visual formatting的更多細節進行學習。(又是一個坑...)
最後更新:2017-04-03 05:40:04