瞭解BFC後,可以更深刻的明白外邊距合併原理
。
瞭解BFC後,可以更深刻的明白浮動的行爲
。
瞭解BFC後,知識就是你的,總不會吃虧對吧?哈哈css
以前有兩篇文章《行內元素的一些探索》、《浮動的一些探索》,或多或少隱式的牽連到了塊級格式化上下文
。html
今天正式介紹BFC
,以加深
對CSS的理解。程序員
-------------寫在前面---------------BFC是時候表演真正的技術了
以前的內容是參考MDN的塊級格式化上下文和自我思考的過程組成,今日重讀感受糟糕之極,因而便在此給出w3c規範中的定義以做補充。segmentfault
W3C規範中是這樣定義BFC的:瀏覽器
Floats, absolutely positioned elements,
block containers
(such as inline-blocks, table-cells, and table-captions) that are notblock boxes
, and block boxes with 'overflow' other than 'visible' (except when that value has been propagated to the viewport) establish new block formatting contexts fortheir contents
.ide
這裏須要注意的幾個點block containers
、block boxes
、their contents
。佈局
浮動、絕對定位元素,非塊級盒子的
塊級容器
(好比display屬性值爲inline-block,tale-cells和table-captions的元素),以及overflow屬性設置爲visible之外的塊級盒子
都爲它們的內容
建立了新的塊級格式化上下文。優化
原文在這
block formatting contextui
完全瞭解w3c規範BFC的定義後能夠直接閱讀最後一部份內容(bfc是時候表演技術)idea
塊級格式化上下文(block formatting context)是頁面CSS視覺渲染的一部分。它是用於
決定
盒子的佈局及浮動相互影響範圍的一個區域
。BFC之間
不會相互影響。
一共三句話。
第一句話忽略不計。
第二句話很關鍵。不過先反問下,這句話裏的
盒子
指的是誰,也就是說決定誰的浮動和佈局?
這個問題的答案其實很簡單,這裏的盒子指代的是產生BFC元素
的塊級子元素
。
反思:什麼條件產生BFC,決定誰的佈局?
解答:BFC的產生見下文。塊級格式化上下文(BFC)從字面上理解應該能推測出盒子指代的是塊級子元素
。
第三句話也很重要。BFC之間不會相互影響,那麼
BFC
和其它的元素會發生影響,BFC中的元素
和其它的元素會發生影響嘛?
答案是確定的,BFC和其它元素會發生影響,但BFC中的元素
不會受其它元素影響。
反思:這裏的其它元素
指的是什麼元素?
解答:處在同一個包含塊中同級別(互爲兄弟
關係)的元素。
官方外衣老是華麗,值得琢磨,儘管仔細思考了也必然有所獲。但,做爲程序員,怎能不去了解她的心。
塊級格式化上下文包括了建立該上下文的元素的
全部子元素
,但不包括建立了新的塊格式化上下文
的子元素。
若是世界的運行都有自我運行的一套機制,那麼BFC的世界必然有本身的一套規則。世界的運行總避免不了能力超凡者打破規矩、突破限制造成另外一個世界並再也不受以前的約束。這裏的能力超凡者就能夠理解爲建立了新的塊級格式化上下文
的元素。那麼若是得到創造新世界的能力呢?
世界歷來都不是平等的,或許就像海賊王裏的天龍人,高高在上。BFC有些元素與生俱來,有些就靠開發者來賦予。
根元素或其它包含它的元素
浮動和定位元素(設置了float不爲none或position不爲relative和static)
overflow不爲visible的元素
display設爲inline-block、table-cell和table-caption
新的世界,總會造成一套本身的規矩。
在BFC中,盒子
從頂端開始垂直地一個接一個排列,兩個盒子之間的垂直間隙是由他們的margin值決定
在同一
個BFC中,兩個相鄰塊級盒子的垂直外邊距會產生重疊
在BFC中,每個
盒子的左邊緣都會觸碰到容器到左邊緣
計算BFC元素高度時,浮動元素也參與計算
這是一種常見的現象。有些時候,實際須要的效果是下面這樣的。
爲了直觀的看到BFC的做用,給段落p添加個背景色。
清晰看見p的行框
有一部分是在綠色透明塊之下的,如今賦予p元素BFC的話,那麼浮動的Div和p就至關於超強者(各自造成本身的BFC),根據BFC之間互不影響這一特性。效果就是下面這樣的。
當時在回答div外邊距的問題時,總結出了合併的一條規則:margin必須相鄰。但當時只探究了margin合併的條件,卻不曾探究條件的產生(什麼狀況纔算相鄰)。如今從BFC的角度重聊這個話題。
margin相鄰的前提條件
必須處於文檔正常流
的塊級盒子
,而且處於同一個BFC
沒有線盒,沒有清除區域(clearance),沒有padding和border
margin都是垂直方向上的
針對第一點,進行一個簡單的說明。
這是常見的外邊距合併。觸發合併的因素符合相鄰的前提條件。
代碼以下
<style type="text/css"> body{margin:0;padding:0;} .parent{width:400px;height:400px;background-color:#af3;margin:20px 0;} .child{width:200px;height:200px;margin:30px 0;background-color:#f3a;} </style> </head> <body> <div class="parent"> <div class="child"></div> </div> </body>
通常狀況阻止外邊距重疊會給父元素Div.parent
設置overflow:hidden。
那麼若是給Div.child
設置overflow:hidden呢?
.child{width:200px;height:200px;margin:30px 0;background:#f3a;overflow:hidden;}
怎麼會這樣?Div.child
不是產生了新的BFC嗎?那麼Div.parent
和Div.child
就不在同一BFC中了。那麼不是違背了margin相鄰的前提條件中的第一條了嗎?爲何還發生重疊了?
反思:重疊既然發生了,說明一個問題,那就是margin相鄰的條件所有知足了。這說明Div.parent
和Div.child
處在同一個BFC,overflow:hidden設置給父元素
時確實發生做用併產生了新的BFC,並阻止重疊。
overflow:hidden設置給子元素
時也確實做用併產生新BFC,但卻沒阻止重疊。
說到這,須要考慮的問題就是BFC概念了。
在BFC的心
這一節裏有提BFC的內部組成。這裏再次引用:
塊級格式化上下文包括了建立該上下文的元素的
全部子元素
,但不包括建立了新的塊格式化上下文
的子元素。
一句話歸納:BFC是爲其內容而生的。
BFC不包括產生BFC的元素,也就是不包括生產者!
overflow:hidden設置給父元素時,阻斷了重疊發生。這說明Div.parent
和Div.child
不處在同一個BFC中。child處在parent產生的BFC中,而parent處在最開始的BFC中。因此重疊不發生。
overflow:hidden設置給子元素時,重疊發生。這說明Div.parent
和Div.child
處在同一個BFC中。child產生的BFC並不會把child本身也歸入進去,而是做用於child的子元素。所以parent和child依然處於同一個BFC中,因此重疊發生。
小白提問
考慮下Div.parent和Div.child單獨設置display:inline-block時,會發生什麼現象?
答案或許是這樣的,和overflow同樣唄。
實踐纔出真理。
不管是給父元素仍是子元素設置display:inline-block,重疊都不會發生。爲何呢?不都是產生了BFC嗎?
小白揭祕
margin相鄰前提條件的第一條裏是要求必須是塊級盒子
,而在w3c規範中規定,塊級盒子的display屬性必須是如下三種之一:'block', 'list-item', 和 'table'。display:inline-block不在其列,於是不管給誰設置display都不知足條件。因此重疊不發生。
下面代碼中的margin-top
方向爲何沒有產生下移的視覺效果,而margin-left
方向竟然產生了左移的效果。
<style type="text/css"> /*body { margin: 0; padding: 0; }*/ section { background-color: #af3; } .float { float: left; border: 5px solid #fe3; width: 200px; } .child { clear: both; margin: 1000px 200px; border: 5px solid; width: 200px; height: 200px; background-color: #f3a; } </style> </head> <body> <section> <div class="float"></div> <div class="child"></div> </section> </body>
這個問題困惑了我好久。淺談我的對該問題的理解:
在閱讀前,最好深刻margin屬性。若是不肯意看的話,那就直接看下面這段話吧。
在 margin 中 top、right、bottom、left 的參考線並不一致爲一類,而是分爲了兩類參考線。top 和 left 的參考線屬於一類,right 和bottom 的參考線屬於另外一類。top 以
containing block
的content
上邊或者垂直上方相連元素margin
的下邊爲參考線垂直向下位移;left 以containing block
的content
左邊或者水平左方相連元素margin
的右邊爲參考線水平向右位移。right 以元素自己的 border 右邊爲參考線水平向右位移;bottom 以元素自己的border 下邊爲參考線垂直向下位移。
小白分析
這段話首先將trbl分紅了兩類。
其中top和left的參考線又分爲兩種。
container block content top(left)
top和left分別對應父元素
的content top edge
或content left edge
sibling margin-bottom (margin-right)
top和left分別對應相鄰元素
的margin-bottom
或margin-right
(這裏固然會發生margin collapse)
bottom和right是以元素自身
的border-bottom
(border-right)爲邊界。
問題剖析
1.margin-left發生了水平偏移,且child在水平方向
並無相鄰元素
,這說明其參考是父元素
(section)的content left edge
。
2.margin-top沒發生偏移,這至少說明其參考線不是父元素
(section)的content top edge
。然而在垂直方向上,child是擁有一個相鄰的東西(clearance
),那麼很明顯就是clearance搗鬼。若是clearance被解析成盒模型了,那麼垂直方向確定能產生位移,若是解析成其餘一些東西了,那麼沒有位移就很正常了。 (這一點也模糊,但願有人指點)
兩列布局的要求:
一欄寬度固定
一欄寬度隨瀏覽器發生改變
如下最多見的代碼,沒有任何問題。
<style type="text/css"> body,aside,main { margin: 0; padding: 0; } aside { float: left; width: 200px; height: 50px; background-color: #3fa; } main { height: 50px; text-align: center; background-color: #af3; } </style> </head> <body> <aside>This is aside</aside> <main>This is main content</main> </body>
換個思路實現兩列布局,各位看官看好嘍。
<style type="text/css"> body,aside,main { margin: 0; padding: 0; } aside { float: right; width: 200px; height: 50px; background-color: #3fa; } main { float: right; margin-left: -200px; width: 100%; height: 50px; text-align: center; background-color: #af3; } </style> </head> <body> <main>This is main content</main> <aside>This is aside</aside> </body>
這裏作了三處修改
將main與aside的位置交換
爲main添加右浮動
,改變aside浮動方向
,並顯式
設置了width:100%
爲main添加了margin-left
:-200px;
這樣看上去是沒有任何一點問題的。
若是向main裏添加更多的文字會發生什麼?
那麼第一種兩列布局會不會發生上面這種狀況呢?
你真的懂BFC了嗎?
小白解析
第一種佈局之因此不會發生重疊,是由於它們在同一個BFC中
第二種佈局之因此會重疊,是由於它們在不一樣BFC中
問題產生
<style type="text/css"> body,.left,.right,main { margin: 0; padding: 0; text-align: center; } main { float: left; width: 100%; height: 50px; background-color: #3fa; } .left { float: left; margin-left: -100%; width: 250px; height: 50px; background-color: #af3; opacity: 0.4; } .right { float: left; margin-left: -100px; width: 100px; height: 50px; background-color: #f3a; opacity: 0.4; } </style> </head> <body> <div> <main>I am boss</main> <aside class="left">Left</aside> <aside class="right">right</aside> </div> </body>
基本描述:此時給中間綠色塊
div填寫內容會出現重疊現象,原理同上述兩列布局
爲了解決這個問題,出現兩種思路(或許雙飛翼是聖盃的優化方案)
聖盃好像很好吃的樣子~
<style type="text/css"> body,.left,.right,main { margin: 0; padding: 0; text-align: center; } div { padding: 0 100px 0 250px; } main { float: left; width: 100%; height: 50px; background-color: #3fa; } .left { position: relative; left: -250px; float: left; margin-left: -100%; width: 250px; height: 50px; background-color: #af3; opacity: 0.4; } .right { position: relative; right: -100px; float: left; margin-left: -100px; width: 100px; height: 50px; background-color: #f3a; opacity: 0.4; } </style> </head> <body> <div> <main>I am boss</main> <aside class="left">Left</aside> <aside class="right">right</aside> </div> </body>
以上就是聖盃佈局
。分析就不寫了。直接看雙飛翼佈局吧,很是精妙的佈局。
<style type="text/css"> body,.left,.right,main { margin: 0; padding: 0; text-align: center; } .wrap-main { float: left; width: 100%; } main { margin: 0 100px 0 250px; height: 50px; background-color: #3fa; } .left { float: left; margin-left: -100%; width: 250px; height: 50px; background-color: #af3; opacity: 0.4; } .right { float: left; margin-left: -100px; width: 100px; height: 50px; background-color: #f3a; opacity: 0.4; } </style> </head> <body> <div> <div class="wrap-main"> <main>I am bossI am bossI am bossI am bossI am bossI am bossI am bossI am bossI am bossI am bossI am bossI am bossI am bossI am bossI am bossI am bossI am bossI am bossI am bossI am bossI am bossI am bossI am bossI am bossI am bossI am bossI am boss</main> </div> <aside class="left">Left</aside> <aside class="right">right</aside> </div> </body>
小白分析
首先在不考慮使用position:relative的狀況下,要達到聖盃佈局的效果,那隻能經過margin來做用。先貼張問題圖方便閱讀
那麼直接給main(綠色的div)添加margin: 0 100px 0 250px;不就好了嗎?
由上述兩張圖可知道margin屬性只有左邊的發生做用了,而右邊的未發生做用。這是由於屬性過度受限(overconstrained)
,將margin-right強制設爲auto了。(調試器看不出來的)
所以該思路不可行。
因而便思索經過水平格式化
這一idea來實現。
給main套一層div.wrap-main
將main的部分屬性(float、width)移到div.wrap-main
,併爲main
添加margin: 0 100px 0 250px
;
至此,雙飛翼佈局已經實現。
涉及知識點總結:
水平格式化
屬性過度受限(overconstrained)
margin屬性深刻了解
bfc的深刻認識
float的深刻了解
最後一點小收穫:
這張圖應該頗有印象吧,前面有喲。
爲何這裏有個空白?aside.right
在哪裏呢?
儘管main的margin-right水平受限被設置爲auto,但在計算和渲染時,100px仍然會發生做用。因此aside.left
的margin-left:-100%不是靠着最左邊而是間隔了100px。受其影響,aside.right
的margin-left:-100px就把本身藏在了最左邊(下圖的位置)。以上純屬我的理解