關於BFC理解

call_me_R發表於2019-02-27
frontend/css/bfc/banner

BFC(Block Formatting Context,塊格式上下文)是Web頁面的視覺化CSS渲染的一部分,是佈局過程中生成塊級盒子的區域,也是浮動元素與其他元素的互動限定區域。對其的理解能夠幫你更好的佈局頁面。 原文連結請戳這裡

常見的定位方案

在進入BFC話題前,先來捋一下常見的定位方案,定位方案是控制元素的佈局,主要有三種常見的方案:

普通流(正常文件流)

在普通流中,元素按照其在HTML的先後位置至上而下佈局,在這個過程中,行內元素水平排列,直到當行被佔滿然後換行,塊級元素則被渲染為完整的一個新行,除非另外指定,否則所有元素預設都是流定位。也可以說,普通流中元素的位置由該元素在HTML文件中的位置決定。

額外:⚠️

HTML中,元素可以分成塊級元素(block-level elements)和行內元素(inline-level elements)。具體的解析會另開一篇博文出來講解。

下面給出普通流的一個例子:

<body>
	<span>first</span>
	<a href="javascript:;">second</a>
	<div class="demo demo1"></div>
	<div class="demo demo2"></div>
</body>
複製程式碼
html,body{
	margin: 0;
	padding: 0;
}
.demo{
	width: 200px;
	height: 200px;
}
.demo1{
	background: red;
}
.demo2{
	background: blue;
}
複製程式碼
frontend/css/bfc/normal-flow

浮動

在浮動佈局中,元素首先按照普通流的位置出現,然後根據浮動的方向儘可能的向左邊或右邊偏移,其效果和印刷排版中的文字環繞相似。如下:

<body>
	<div class="container">
		<div class="left"></div>
		文字內容文字內容文字內容文字內容文字內容文字內容文字內容文字內容文字內容文字內容文字內容文字內容文字內容文字內容文字內容文字內容文字內容文字內容
	</div>
</body>
複製程式碼
html,body{
    margin: 0;
    padding: 0;
}
body{
    padding: 100px;
}
.container{
    border: 1px dotted red;
}
.container .left{
    width: 50px;
    height: 50px;
    background: blue;
    float: left;
}
複製程式碼
frontend/css/bfc/float-flow-img

⚠️為什會內容環繞呢,而不是跟浮動元素重合呢?

《CSS權威指南》中指出,浮動的目的,最初只能用於影像,的就是為了允許其他內容(如文字)“圍繞”該影像。而後來的CSS允許浮動任何元素。

絕對定位

在絕對定位佈局中,元素會整體脫離普通流(浮動佈局可以理解成脫離父元素文字流),因此絕對定位不會對其兄弟元素造成影響,而具體的位置由絕對定位的座標決定。

什麼是BFC

Formatting context(格式上下文)是W3C CSS2.1規範中的一個概念。它是頁面中的一塊渲染區域,並且有一套渲染規則,它決定了其子元素將如何定位,以及和其他元素的關係和相互作用。

BFC即Block Formatting Content(塊級格式上下文),它屬於上述定位方案的普通流。具有BFC特性的元素可以看作是**隔離了的獨立容器,容器裡面的元素不會在佈局上影響到外面的元素,並且BFC具有普通容器所沒有的一些特性。**後面介紹。

簡單來說,可以把BFC理解成一個封閉的大箱子,箱子內部的元素無論如何翻江倒海,都不會影響到外部的元素。

觸發BFC的條件

下面的方式會建立塊格式上下文

  • 根元素或包含根元素的元素,這裡我理解為body元素
  • 浮動元素(元素的float不是none
  • 絕對定位元素(元素的positionabsolutefixed
  • 行內塊元素(元素的displayinline-block
  • 表格單元格(元素的displaytable-cell,html表格單元格預設為該值)
  • 表格標題(元素的displaytable-caption,html表格標題預設為該值)
  • 匿名錶格單元格元素(元素的displaytabletable-rowtable-row-grouptable-header-grouptable-footer-group(分別是html table、row、tbody、thead、tfoot的預設屬性)或inline-table
  • overflow值不為visible的塊元素
  • display值為flow-root的元素
  • contain值為layout、content或strict的元素
  • 彈性元素(displayflexinline-flex元素的直接子元素)
  • 網格元素(displaygridinline-grip元素的直接子元素)
  • 多列容器(元素的column-countcolumn-width不為auto,包括column-count為1)
  • column-spanall的元素始終會建立一個新的BFC,即使該元素沒有包裹在一個多列容器中

建立了塊格式上下文的元素中的所有內容都會被包含在BFC中。以上的建立方式參考自塊格式化上下文

BFC的特性(作用)

簡單羅列下BFC的特性:

  1. 內部的box會在垂直方向,從頂部開始一個接一個地放置
  2. box垂直方向的距離由margin決定。屬於同一個BFC的兩個相鄰的box的margin會發生疊加,結果值並集
  3. 在BFC中,每個盒子的左外邊緣(margin-left)會觸碰到容器的左邊緣(border-left)。(對於從右到左的格式來說,則觸碰到右邊緣),即使是浮動也是如此。即不會發生margin穿透
  4. 形成了BFC的區域不會與float box重疊(可阻止因浮動元素引發的文字環繞現象)
  5. 計算BFC高度時,浮動元素也參與計算(BFC會確切包含浮動的子元素,即閉合浮動)

注意⚠️,對於第五點,原本浮動元素是應該脫離文件流的,但是BFC中會計算其高度。

BFC的一些應用

實現自適應的兩欄佈局

應用了第四點BFC的區域不會與float box重疊的特性。一邊浮動,另一邊自適應的部分形成BFC,那麼兩者就不會重疊,避免了文字環繞及類似情形。

<body>
	<div class="container">
        <div class="left">left</div>
        <div class="right">right</div>
	</div>
</body>
複製程式碼
html,body{
    margin: 0;
    padding: 0;
}
body{
    padding: 50px;
}
.container {
    width: 100%;
}
.left {
    width: 100px;
    height: 150px;
    background-color: blue;
    float: left;
}
.right {
    height: 200px;
    background-color: red;
    overflow: hidden; /*把.right這個自適應的box變成BFC,避免與.left box這個有float屬性的元素重疊*/
}
複製程式碼
frontend/css/bfc/responsive-two-column

解決父元素高度塌陷

高度塌陷產生的原因:父元素未設定固定的高度,完全由子元素高度撐開;當子元素float之後脫離了文件流,父元素內部就沒有支撐,造成了高度的塌陷

解決這種問題我上之前的博文中有總結,地址請戳CSS清除浮動。這裡再簡單的說下原理:

也就是用到了BFC的特性五:閉合內部浮動

<body>
	<div class="container">
	    <div class="left">left</div>
	</div>
</body>
複製程式碼
html,body{
    margin: 0;
    padding: 0;
}
.container {
    width: 100%;
    border: 1px solid red;
    overflow: hidden; /*形成BFC,使其能閉合浮動 box*/
}
.left {
    width: 100px;
    height: 150px;
    background-color: blue;
    float: left;
}
複製程式碼

margin重疊解決

在正常的文件流中,在垂直方向上兄弟box的margin會取交集(取最大的顯示),為神馬會出現這種情況呢?可以說是一種bug,也可以說是一種規範。我比較傾向於後者。

<body>
    <div class="top">top</div>
    <div class="bottom">bottom</div>
</body>
複製程式碼
.top {
    width: 100px;
    height: 100px;
    background-color: blue;
    margin-bottom: 100px;
}
.bottom{
    width: 100px;
    height: 100px;
    background-color: red;
    margin-top: 50px;
}
複製程式碼
frontend/css/bfc/margin-vertical-before

上面出現的情況是BFC的特性三:屬於同一個BFC的兩個相鄰的box的margin會發生疊加,結果值並集。那麼,我們將他們隔離成不同的BFC不久解決問題了嘛。

<body>
    <div class=`wrap`>
        <div class="top">top</div>
    </div>
    <div class="bottom">bottom</div>
</body>
複製程式碼
.top {
    width: 100px;
    height: 100px;
    background-color: blue;
    margin-bottom: 100px;
}
.bottom{
    width: 100px;
    height: 100px;
    background-color: red;
    margin-top: 50px;
}
.wrap{
    overflow: hidden; /*將.top box包含在另外一個BFC中隔離開*/
}
複製程式碼
frontend/css/bfc/margin-vertical-after

後話

使用好BFC能夠是你更好的佈局。文中部分內容參考網路內容。

各位大佬輕噴,還請多多指正。放上github地址github.com/reng99求指教就逃:)

other/loveU