天天看點

由position屬性引申的關于css的進階讨論(包含塊、BFC、margin collapse)

面試的時候問個css的position屬性能刷掉一半的人這是啥情況…… 其實這問題我本來打算的是可以順着一路扯到normal flow、containing block、bfc、margin collapse,base line,writing mode,bidi,這樣一路問下去的,奈何第一個問題(親我真的隻問了position有哪些取值和行為啊)就悲劇了……

說到position,那麼稍微對css有所了解的必然能馬上說出它的四個屬性值:static 、relative、absolute、fixed。但是更深一步去讨論,牽扯出諸如上文提到的normal flow、containing block、bfc、margin collapse,base line,writing mode,bidi,又有多少人能很好的回答完整呢,是以我想在此做一個自己的總結歸納。

1.normal flow         

normal flow(正常流):正常流是預設的定位方式。任何沒有具體指定{position:absolute}或者{position:fixed}屬性以及沒有被浮動的元素都将預設獲得此屬性。

在這種方式裡,塊級元素在它們的包含塊裡一個一個垂直延伸,行内元素在它們的包含塊裡從左至右的水準排布。

值得注意的是,在正常流裡垂直邊距(vertical margin)是重疊的。也就是說,上下兩個塊級盒之間的邊距由它們之中邊距較大的元素決定,而不是他們的和!

效果顯示如下:

由position屬性引申的關于css的進階讨論(包含塊、BFC、margin collapse)

div1和div2的垂直距離由大的margin決定,也就是div2的40px而不是二者之和60px。

其次,行内元素是會被折斷的,當寬度受到限制的時候,它會自動移動到下一行。這可能會産生一些難看的效果如果行内塊有邊框的話。看下面的效果:

由position屬性引申的關于css的進階讨論(包含塊、BFC、margin collapse)

2.containing block        

containing block(包含塊):是視覺格式化模型的一個重要概念,它與框模型類似,也可以了解為一個矩形,而這個矩形的作用是為它裡面包含的元素提供一個參考,元素的尺寸和位置往往是由該元素所在的包含塊決定的。也就是說一個元素盒子的位置和大小有時是通過相對于一個特定的長方形來計算的,這個長方形就被稱之為元素的 containing block。

一個元素的containing block按照以下方式定義:

使用者代理(比如浏覽器)選擇根元素作為 containing block(稱之為初始 containing block)。

對于其它元素,除非元素使用的是絕對位置,containing block 由最近的塊級祖先元素盒子的内容邊界組成。

如果元素有屬性 'position:fixed',containing block 由視口建立。

如果元素有屬性 'position:absolute',containing block 由最近的 position 不是 static 的祖先建立,按下面的步驟:

如果祖先是塊級元素,containing block 由祖先的 padding edge 形成。

如果祖先是内聯元素,containing block 取決于祖先的 direction 屬性。

如果 direction 是 ltr(左到右),祖先産生的第一個盒子的上、左内容邊界是 containing block 的上方和左方,祖先的最後一個盒子的下、右内容邊界是 containing block 的下方和右方。

如果 direction 是 rtl(右到左),祖先産生的第一個盒子的上、右内容邊界是 containing block 的上方和右方,祖先的最後一個盒子的下、左内容邊界是 containing block 的下方和左方。

  5.如果沒有祖先,根元素盒子的内容邊界确定為 containing block。

名詞解釋: 視口:通過解析文檔,連續媒體(比如螢幕就是連續媒體,而列印機則是基于頁的媒體)給使用者産生一個視口(一個視窗或其它在螢幕上顯示的區域)。 根元素:源檔案中,每一個元素都有一個父元素,隻有一個例外,它就是根元素。 padding edge:請參見下圖:
由position屬性引申的關于css的進階讨論(包含塊、BFC、margin collapse)

舉個闆栗:

那麼,在沒有指定任何position的情況下,上方代碼的containng block确定方式為:

由position屬性引申的關于css的進階讨論(包含塊、BFC、margin collapse)

如果我們設定div1的position為:

此時,div1 的 containing block 就不再是 body,它變成了初始 containing block(因為這裡還沒有具有 position 的祖先盒子)。

3.BFC        

BFC(Block Formatting Context 塊格式化上下文):是W3C CSS 2.1 規範中的一個概念,在CSS3中被修改為flow root。格式化則表明了在這個環境中,元素處于此環境中應當被初始化,即元素在此環境中應當如何布局等。元素如果建立了BF麼BFC決定了如何對其内容進行定位,以及它與其他元素的關系和互相作用。

通俗了解:首先BFC是一個名詞,是一個獨立的布局環境,我們可以了解為一個箱子(實際上是看不見摸不着的),箱子裡面物品的擺放是不受外界的影響的。轉換為BFC的了解則是:BFC中的元素的布局是不受外界的影響(我們往往利用這個特性來消除浮動元素對其非浮動的兄弟元素和其子元素帶來的影響。)并且在一個BFC中,塊盒與行盒(行盒由一行中所有的内聯元素所組成)都會垂直的沿着其父元素的邊框排列。

建立了BFC的元素會按照如下的方式對其子元素進行排列:

在BFC中,盒子從頂端開始垂直地一個接一個地排列,兩個盒子之間的垂直的間隙是由他們的margin 值所決定的。在一個BFC中,兩個相鄰的塊級盒子的垂直外邊距會産生折疊。即是在BFC中相鄰的塊級元素的垂直邊距會折疊(collapse)。

在BFC中,每一個盒子的左外邊緣(margin-left)會觸碰到容器的左邊緣(border-left)(對于從右到左的格式來說,則觸碰到右邊緣), 即使存在浮動也是如此(盡管一個子元素的内容區域會由于浮動而壓縮),除非這個子元素也建立了一個新的BFC,如它自身也是一個浮動。

邊距折疊:在CSS當中,相鄰的兩個盒子(可能是兄弟關系也可能是祖先關系)的外邊距可以結合成一個單獨的外邊距。這種合并外邊距的方式被稱為折疊,并且因而所結合成的外邊距稱為折疊外邊距。

折疊的結果: 

兩個相鄰的外邊距都是正數時,折疊結果是它們兩者之間較大的值。

兩個相鄰的外邊距都是負數時,折疊結果是兩者絕對值的較大值。

兩個外邊距一正一負時,折疊結果是兩者的相加的和。

産生折疊的必備條件:margin必須是鄰接的,且需要滿足如下條件:

必須是處于正常文檔流(非float和絕對定位)的塊級盒子,并且處于同一個BFC當中。

沒有線盒,沒有空隙(clearance),沒有padding和border将他們分隔開

都屬于垂直方向上相鄰的外邊距,可以是下面任意一種情況

元素的margin-top與其第一個正常文檔流的子元素的margin-top

元素的margin-bottom與其下一個正常文檔流的兄弟元素的margin-top

height為auto的元素的margin-bottom與其最後一個正常文檔流的子元素的margin-bottom

高度為0并且最小高度也為0,不包含正常文檔流的子元素,并且自身沒有建立新的BFC的元素的margin-top和margin-bottom

那麼,什麼情況下會建立BFC:  

浮動元素(float: left | right)

絕對定位元素(position: absolute | fixed)

行内塊元素(display: inline-block)

表格的單元格(display: table-cells,TD、TH)

表格的标題(display: table-captions,CAPTION)

'overflow' 特性不為 visible 的元素

表格元素建立的 "匿名框"

值得注意的是,"display:table" 本身并不産生 "block formatting contexts"。但是,它可以産生匿名框, 其中包含 "display:table-cell" 的框會産生塊格式化上下文。

總之,對于 "display:table" 的元素,産生塊格式化上下文的是匿名框而不是 "display:table"。

最後,是這些元素建立了塊格式化上下文,它們本身不是塊格式化上下文。

BFC的運用:消除浮動與多欄布局。

1.自适應兩欄布局

效果如下:

由position屬性引申的關于css的進階讨論(包含塊、BFC、margin collapse)

根據BFC的規則:

每一個盒子的左外邊緣(margin-left)會觸碰到容器的左邊緣(border-left)(對于從右到左的格式來說,則觸碰到右邊緣)

是以,雖然存在浮動的元素aslide,但main的左邊依然會與包含塊的左邊相接觸。

是以,我們可以根據:

BFC的區域不會與float box重疊。

來通過觸發main生成BFC,實作自适應兩欄布局。

當觸發main生成BFC後,這個新的BFC不會與浮動的aside重疊。是以會根據包含塊的寬度,和aside的寬度,自動變窄。效果如下:

由position屬性引申的關于css的進階讨論(包含塊、BFC、margin collapse)

2.清除内部浮動

希望的結果是:

由position屬性引申的關于css的進階讨論(包含塊、BFC、margin collapse)

但結果是:

由position屬性引申的關于css的進階讨論(包含塊、BFC、margin collapse)

父容器并沒有把浮動的子元素包圍起來,俗稱塌陷,為了消除這種現象,除了用傳統的僞類方法。根據

計算BFC的高度時,浮動元素也參與計算

還可以使父容器形成BFC,來清除浮動,簡單修改一下代碼:

可以得到如下效果:

由position屬性引申的關于css的進階讨論(包含塊、BFC、margin collapse)

總結來說,BFC就是頁面上的一個隔離的獨立容器,容器裡面的子元素不會影響到外面的元素。反之也如此。

因為BFC内部的元素和外部的元素絕對不會互相影響,是以,當BFC外部存在浮動時,它不應該影響BFC内部Box的布局,BFC會通過變窄,而不與浮動有重疊。同樣的,當BFC内部有浮動時,為了不影響外部元素的布局,BFC計算高度時會包括浮動的高度。避免margin重疊也是這樣的一個道理。

繼續閱讀