前言
總括: 對于盒子模型,BFC,IFC和外邊距合并等概念和問題的總結(jié)
原文地址:從CSS盒子模型說起
知乎專欄:前端進(jìn)擊者
博主博客地址:Damonare的個(gè)人博客
為學(xué)之道,莫先于窮理;窮理之要,必先于讀書。
正文
? CSS盒子模型是CSS基礎(chǔ)中的基礎(chǔ),個(gè)人之前對于這塊的理解有偏差??,由于涉及知識點(diǎn)比較多所以寫一篇總結(jié)備忘。
之前打算的是兩周一次更新博文的,但是時(shí)間用在了刷題上,做了很多l(xiāng)eetcode上算法數(shù)據(jù)結(jié)構(gòu)的題記錄在github,但其實(shí)也有在更新啦~只不過是對之前的一些博文進(jìn)行糾正:)??
? 最近秋招在即,壓力倍增,前幾天把博客導(dǎo)入頁的在讀大三本科生
改為了在讀大四本科生
,不禁心生感慨,時(shí)光荏苒。轉(zhuǎn)眼間我的這個(gè)小窩竟然已經(jīng)快一年了。當(dāng)初建立這個(gè)站點(diǎn)也是想找個(gè)說話的地方,有的人可能就是不喜歡說,只喜歡寫(比如我),然而自從實(shí)習(xí)后確實(shí)提不起精神來寫了,一是沒精力,二是對于遇到的一些坑不想單獨(dú)寫一篇博客記錄。這里還是想保持一份純凈,就是以總結(jié)和理解難點(diǎn)為主調(diào) 其它的一般會托管在github庫里記錄一下。閑話不多說,說說今天的主角???♀?
? CSS盒子模型想來都不陌生,但還是想先介紹一下,以保證文章的完整性。??
盒子模型
? CSS盒子模型:

在一個(gè)文檔中,每一個(gè)元素都被抽象成一個(gè)盒子,每一個(gè)盒子又包括四部分(從內(nèi)到外):內(nèi)容(content),內(nèi)填充(padding),邊框(border),外邊距(margin)。見上圖,這是從二維的角度分析,來張三維立體圖:??
此圖很形象的解釋了CSS盒子的構(gòu)成:
- content box:立體盒子的核心
- padding box:內(nèi)邊距區(qū)域padding area 延伸到包圍padding的邊框。如果內(nèi)容區(qū)域content area設(shè)置了背景、顏色或者圖片,這些樣式將會延伸到padding上(當(dāng)然我們可以通過background-clip設(shè)置作用區(qū)域)
- border box:由border和4條border edge組成。若border寬度設(shè)置為0,則border edge與padding edage重疊;
- margin box:由margin和4條margin edge組成。若margin寬度設(shè)置為0,則margin edge與border edage重疊。
??看起來很復(fù)雜的樣子...
拿PS圖層的概念更好理解這塊,最上面的就是content box往下一次是padding box,border box,margin box。
那么盒子模型一般分為兩種:
IE盒子模型
所謂IE盒子模型,就是之前IE瀏覽器實(shí)現(xiàn)的一種怪異的盒子模型,怎么怪異呢?當(dāng)我們這樣設(shè)置的時(shí)候:
div {
width: 100px;
height: 100px;
}
理論上我們想要設(shè)置的就是content box的寬高嘛,但是IE在解析的時(shí)候會按照這個(gè)規(guī)則解析:
width = content-width + padding-width + border-width
height = content-height + padding-height + border-height
這就導(dǎo)致了這種尷尬的境地:下面無內(nèi)容的話直接戳這里??
<script async src="https://jsfiddle.net/Damonare/f7vh400t/embed/html,css,result//"></script>
標(biāo)準(zhǔn)盒子模型
標(biāo)準(zhǔn)就比較符合常人的思維了,設(shè)置的width,height就是content的width和height
規(guī)則就是:
width = content-width
height = content-height
實(shí)例如下:無內(nèi)容戳這??
<script async src="https://jsfiddle.net/Damonare/a6oogyyz/embed/html,css,result/"></script>
可能秉著寬大為懷的準(zhǔn)則,CSS3加了個(gè)box-sizing屬性,變相承認(rèn)了這兩種盒子都對(好吧,可能一個(gè)人有一個(gè)人的看法吧),不過box-sizing默認(rèn)屬性就是content-box,即標(biāo)準(zhǔn)盒子模式,IE盒子模型呢,是屬性border-box。剛剛查MDN發(fā)現(xiàn)還有一個(gè)屬性padding-box(width=content-width+padding-width),不過并沒有瀏覽器實(shí)現(xiàn)它(真可憐),并無卵用??
行內(nèi)元素的思考
剛剛說的是以塊級元素為例說的,那么行內(nèi)元素呢?好吧,其實(shí)你知道,行內(nèi)盒是沒法設(shè)置width和height的,那么之前我就有了這樣的思維定勢:行內(nèi)盒沒有padding,margin,然后發(fā)現(xiàn),哦!行內(nèi)盒是有padding-left,padding-right,margin-left,margin-right的!WOC!,然后又發(fā)現(xiàn),行內(nèi)盒是實(shí)際上身懷八甲...??
<script async src="https://jsfiddle.net/Damonare/826gyrrh/embed/html,css,result/"></script>
行內(nèi)盒子的高由font-size決定的;
行內(nèi)盒子的寬等于其子行級盒子的外寬度(margin+border+padding+content width)之和。
是有padding-top和padding-bottom,margin-left,margin-bottom的但并不占據(jù)空間…這就符合盒子模型了嘛,既都是盒子,自然應(yīng)該是一樣的。行內(nèi)盒的margin-top, margin-bottom不占空間,由此聯(lián)想到了另一個(gè)問題——??
外邊距合并
所謂外邊距合并呢,就是margin合并嘛,看下MDN的定義:
塊的頂部外邊距和底部外邊距有時(shí)被組合(折疊)為單個(gè)外邊距,其大小是組合到其中的最大外邊距,這種行為稱為外邊距合并。
??注意只是上下,沒有說左右。而且是針對塊級元素說的。
外邊距合并有這幾種情況:
相鄰兄弟元素
//HTML
<div class="up">我在上面</div>
<div class="down">我在下面</div>
//CSS
.up {
width: 100px;
height: 100px;
border: 1px solid blue;
margin: 100px;
}
.down {
width: 100px;
height: 100px;
border: 1px solid red;
margin: 100px;
}
我們感性上覺得上下兩個(gè)元素應(yīng)該是相差200px距離,然而并不是。
父子元素
如果塊級父元素中,不存在上邊框、上內(nèi)補(bǔ)、inline content、清除浮動這四條屬性(對于上邊框和上內(nèi)補(bǔ),也可以說,當(dāng)上邊距及上內(nèi)補(bǔ)寬度為0時(shí)),那么這個(gè)塊級元素和其第一個(gè)子元素的上邊距就可以說”挨到了一起“。此時(shí)這個(gè)塊級父元素和其第一個(gè)子元素就會發(fā)生 上外邊距合并 現(xiàn)象,換句話說,此時(shí)這個(gè)父元素對外展現(xiàn)出來的外邊距將直接變成這個(gè)父元素和其第一個(gè)子元素的margin-top的較大者。??
//HTML
<div class="parent">
<div class="child">我是兒子</div>
</div>
//CSS
.parent {
width: 100px;
height: 200px;
background: red;
margin-left: 100px;
}
.child {
width: 50px;
height: 50px;
margin-top: 100px;
border: 1px solid blue;
}
上面代碼感性上可能會覺得,父元素沒有上邊距,然而并不是。
MDN給了三種情況,但第三種空塊元素,我覺得可以包含在這兩種之內(nèi),就沒舉??
那么這種外邊距合并的情況咋解決呢?看下一個(gè)概念...
BFC
??定義:
一個(gè)塊格式化上下文(block formatting context) 是Web頁面的可視化CSS渲染的一部分。它是塊盒子的布局發(fā)生,浮動互相交互的區(qū)域。
那么觸發(fā)BFC的情況有哪些呢?
看MDN:
??一個(gè)塊格式化上下文由以下之一創(chuàng)建:
- 根元素或其它包含它的元素
- 浮動 (元素的
float
不是none
) - 絕對定位的元素 (元素具有
position
為absolute
或fixed
) - 內(nèi)聯(lián)塊 inline-blocks (元素具有
display
: inline-block
) - 表格單元格 (元素具有
display
: table-cell,HTML表格單元格默認(rèn)屬性
) - 表格標(biāo)題 (元素具有
display
: table-caption
, HTML表格標(biāo)題默認(rèn)屬性) - 塊元素具有
overflow
,且值不是visible
-
display
: flow-root
注意,根元素就創(chuàng)建了一個(gè)BFC
那么BFC又有一下特性:
- 內(nèi)部塊級盒子垂直方向排列
- 盒子垂直距離由margin決定,同一個(gè)BFC的盒子外邊距會合并
- BFC就是一個(gè)隔離的容器,內(nèi)部子元素不會影響到外部元素
- 每個(gè)元素的margin box的左邊, 與包含塊border box的左邊相接觸(對于從左往右的格式化,否則相反)。即使存在浮動也是如此。
- BFC的區(qū)域不會與float box疊加。
好,上面外邊距合并的兩種情況,利用BFC如何解決呢?下面沒內(nèi)容的話請戳這里??
<script async src="https://jsfiddle.net/Damonare/ejntapom/embed/html,css,result/"></script>
關(guān)于第四五條特性,請看上面的示例。
BFC用途:
- 清除浮動;
- 解決外邊距合并;
- 布局;
塊級盒子的概念
關(guān)于這塊有好多個(gè)概念...首先是塊級元素和塊級盒子:每個(gè)塊級元素至少生成一個(gè)塊級盒,稱為主要塊級盒。一些元素,比如li,生成額外的盒來放置項(xiàng)目符號,不過多數(shù)元素只生成一個(gè)主要塊級盒。
主要塊級盒將包含后代元素生成的盒以及生成的內(nèi)容。它也是可以使用(定位方案 positioning scheme)的盒。
塊容器盒(block container box) 只包含其它塊級盒,或生成一個(gè)行內(nèi)格式化上下文(inline formatting context)
注意塊級盒與塊容器盒概念不同。 前者描述元素跟它的父元素與兄弟元素之間的表現(xiàn),后者描述元素跟它的后代之間的影響。
同時(shí)是塊容器盒的塊級盒稱為塊盒(block boxes)。(注意塊盒和塊級盒并不是全等)
還有一個(gè)特殊的塊盒——匿名塊盒
<div>Some inline text <p>followed by a paragraph</p> followed by more inline text.</div>
//將創(chuàng)建兩個(gè)匿名塊盒,一個(gè)包含 <p> 前面的文本 (Some inline text), 一個(gè)包含 <p> 后面的文本(followed by more inline text),
塊級元素觸發(fā)BFC,行內(nèi)元素會觸發(fā)啥么?
IFC
IFC 只有在一個(gè)塊級元素中僅包含內(nèi)聯(lián)級別元素時(shí)才會生成。
行內(nèi)盒子的概念
當(dāng)元素的 CSS 屬性 display的計(jì)算值為 inline
, inline-block
或 inline-table
時(shí),稱它為行內(nèi)級元素。視覺上它將內(nèi)容與其它行內(nèi)級元素排列為多行。典型的如段落內(nèi)容,有文本(可以有多種格式譬如著重),或圖片,都是行內(nèi)級元素。
行內(nèi)級元素生成行內(nèi)級盒(inline-level boxes),參與行內(nèi)格式化上下文(inline formatting context)。同時(shí)參與生成行內(nèi)格式化上下文的行內(nèi)級盒稱為行內(nèi)盒(Inline boxes)。所有display:inline 的非替換元素生成的盒是行內(nèi)盒。而不參與生成行內(nèi)格式化上下文的行內(nèi)級盒稱為原子行內(nèi)級盒(*atomic inline-level boxes)。這些盒由可替換行內(nèi)元素,或 display 值為 inline-block
或 inline-table
的元素生成,不能拆分成多個(gè)盒。
另外CSS3還新增了兩種格式上下文:GFC(Grid Formatting Contexts)柵格格式化上下文和FFC(Flex Formatting Contexts)Flex格式化上下文,即分別在元素display為grid和flex、 inline-flex 時(shí)觸發(fā)
定位
常規(guī)流
分清了這些盒子的概念,具體怎么排列呢?以下來自MDN:
在常規(guī)流中,盒一個(gè)接著一個(gè)排列。在塊級格式化上下文里面, 它們豎著排列;在行內(nèi)格式化上下文里面, 它們橫著排列。 當(dāng) position為 static
或 relative
,并且 float 為 none
時(shí)會觸發(fā)常規(guī)流。
浮動(Floats)
對于浮動定位方案(float positioning scheme), 盒稱為浮動盒(floating boxes)。它位于當(dāng)前行的開頭或末尾。這導(dǎo)致常規(guī)流環(huán)繞在它的周邊,除非設(shè)置 clear 屬性。
要使用浮動定位方案,元素 CSS 屬性position 為 static
或 relative
,然后float
不為none
。如果 float
設(shè)為 left
, 浮動由行盒的開頭開始定位。如果設(shè)為 right
, 浮動定位在行盒的末尾。
絕對定位(Absolute positioning)
對于絕對定位方案, 盒從常規(guī)流中被移除,不影響常規(guī)流的布局。 它的定位相對于它的包含塊,相關(guān)CSS屬性:top, bottom, left及right 。
如果元素的屬性position為 absolute
或 fixed, 它是絕對定位元素。
固定定位元素(fixed positioned element)也是絕對定位元素,它的包含塊是視口。當(dāng)頁面滾動時(shí)它固定在屏幕上,因?yàn)橐暱跊]有移動。
以上。