BFC作為前端面試佈局方面的重要考點,開發者有必要進行深入的瞭解,通過對BFC的深入理解,也能幫助我們解決佈局中的很多問題。 ...
在前端的面試中,相對JavaScript而言,CSS佈局方面考察的內容會相對少一些,其中BFC是佈局樣式方面常考的一個考點。
什麼是BFC
BFC,全稱為Block Formatting Context,翻譯過來即塊格式化上下文。
之前在其他文章中看到的說明是,網頁上一個獨立且隔離的渲染區域。現在呢,我稍微查閱了一些官方的信息。
在瞭解BFC之前,我們需要先瞭解一些其他概念:
包含塊(containing block)
containing block
A rectangle that forms the basis of sizing and positioning for the boxes associated with it. Notably, a containing block is not a box (it is a rectangle), however it is often derived from the dimensions of a box. Each box is given a position with respect to its containing block, but it is not confined by this containing block; it can overflow. The phrase “a box’s containing block” means “the containing block in which the box lives,” not the one it generates.
In general, the edges of a box act as the containing block for descendant boxes; we say that a box “establishes” the containing block for its descendants. If properties of a containing block are referenced, they reference the values on the box that generated the containing block. (For the initial containing block, values are taken from the root element unless otherwise specified.)
簡單翻譯一下:
包含塊
一個矩形,是與之相關的盒子的尺寸和定位的基礎。值得註意的是,包含塊並不是一個盒子(它是一個矩形),但它通常是從盒子的尺寸中衍生出來的。每個盒子都有一個相對於其包含塊的位置,但它不受包含塊的限制,可以溢出。“盒子的包含塊"指的是 "盒子所在的包含塊",而不是盒子產生的包含塊。
一般來說,一個盒子的邊緣作為後代盒子的包含塊;我們說一個盒子為它的後代 "建立"了包含塊。如果引用了包含塊的屬性,則引用的是生成包含塊的盒子的值。(對於初始包含塊,除非另有說明,否則其值取自根元素)。
從上述定義中,我們可以簡單認為一個盒子其產生的包含塊就是除了盒子的margin、border和padding以外最內部的那塊區域。
邊緣(edge)
edge
The perimeter of each of the four areas (content, padding, border, and margin) is called an edge
邊緣
四個區域(內容、填充、邊框和外邊距)中每個區域的周長稱為邊緣
邊緣edge,定義了盒模型中的四個邊緣。
initial containing block
The containing block of the root element. The initial containing block establishes a block formatting context.
初始包含塊
指根元素的包含塊。初始包含塊建立了塊格式上下文。
格式化上下文(FC,formatting context)
formatting context
A formatting context is the environment into which a set of related boxes are laid out. Different formatting contexts lay out their boxes according to different rules. For example, a flex formatting context lays out boxes according to the flex layout rules [CSS-FLEXBOX-1], whereas a block formatting context lays out boxes according to the block-and-inline layout rules [CSS2]. Additionally, some types of formatting contexts interleave and co-exist: for example, an inline formatting context exists within and interacts with the block formatting context of the element that establishes it, and a ruby container overlays a ruby formatting context over the inline formatting context in which its ruby base container participates.
A box either establishes a new independent formatting context or continues the formatting context of its containing block. In some cases, it might additionally establish a new (non-independent) co-existing formatting context. Unless otherwise specified, however, establishing a new formatting context creates an independent formatting context. The type of formatting context established by the box is determined by its inner display type. E.g. a grid container establishes a new grid formatting context, a ruby container establishes a new ruby formatting context, and a block container can establish a new block formatting context and/or a new inline formatting context. See the display property.
格式化上下文
格式化上下文是一組相關盒子佈局所在的環境。不同的格式上下文根據不同的規則佈局盒子。例如,彈性格式上下文根據彈性佈局規則[CSS-FLEXBOX-1]佈局方框,而塊格式上下文則根據塊和行內佈局規則[CSS2]佈局盒子。此外,某些類型的格式化上下文會交錯並存:例如,行內格式化上下文存在於建立行內格式化上下文的元素的塊格式化上下文中,並與之交互;ruby容器會在其ruby基本容器所參與的行內格式化上下文上疊加ruby格式化上下文。
盒子要麼建立新的獨立格式上下文,要麼延續其包含塊的格式上下文。在某些情況下,盒子可能會額外建立一個新的(非獨立的)共存格式上下文。除非另有規定,否則建立新的格式上下文會創建一個獨立的格式上下文。盒子建立的格式上下文類型由其內部顯示類型決定。例如,網格容器會建立新的網格格式上下文,ruby容器會建立新的ruby格式上下文,而塊容器可以建立新的塊格式上下文和/或新的行內格式上下文。請參閱顯示屬性。
由上述定義可知每種FC都有其不同於其他FC的佈局規則,由此可知每種FC的創建是聲明瞭一個應用其佈局規則的區域。
而盒子的FC類型由其內部顯示類型決定,即display
屬性的第二個值。
BFC
先看W3C中的定義:Block formatting contexts
Boxes in the normal flow belong to a formatting context, which may be block or inline, but not both simultaneously. Block-level boxes participate in a block formatting context. Inline-level boxes participate in an inline formatting context.
Floats, absolutely positioned elements, block containers (such as inline-blocks, table-cells, and table-captions) that are not block boxes, and block boxes with 'overflow' other than 'visible' (except when that value has been propagated to the viewport) establish new block formatting contexts for their contents.
In a block formatting context, boxes are laid out one after the other, vertically, beginning at the top of a containing block. The vertical distance between two sibling boxes is determined by the 'margin' properties. Vertical margins between adjacent block-level boxes in a block formatting context collapse.
In a block formatting context, each box's left outer edge touches the left edge of the containing block (for right-to-left formatting, right edges touch). This is true even in the presence of floats (although a box's line boxes may shrink due to the floats), unless the box establishes a new block formatting context (in which case the box itself may become narrower due to the floats).
翻譯過來大致是下麵的意思:
正常流中的盒子屬於格式化上下文,可以是塊或行內,但不能同時是塊和行內。塊級盒子屬於塊格式上下文,行內級盒子屬於行內格式上下文。
浮動元素、絕對定位元素、非塊盒子的塊容器(如inline-block、table-cells,以及table-captions),以及 'overflow' 不是 'visible' 的塊盒子(除非該值已傳播到viewport),都會為其內容建立新的塊格式上下文。
在塊格式化上下文中,盒子從包含塊的頂部開始一個接一個地垂直排列。兩個同胞盒子之間的垂直距離由 'margin' 屬性決定。在塊格式上下文中相鄰塊級盒子之間的垂直margin會摺疊。
在塊格式化上下文中,每個盒子的左外緣與包含塊的左緣相接(對於自右向左的格式,則是右緣相接)。即使在浮動的情況下(儘管盒子的行盒可能因浮動而縮小)也是如此,除非盒子建立了新的塊格式化上下文(在這種情況下,盒子自身可能會因浮動而變窄)。
其實看完這個定義我剛開始還有一點疑惑,細想了一下最後一段的意思可以對應以下情況:
前一個是浮動元素,後面緊跟的元素也會與盒子的左緣(或右緣)相接(也就是這個元素與前面的浮動元素會發生重疊),為了防止這個事情發生,可以為後面這個元素創建新的BFC,這樣這個新的BFC就不會與外部BFC的左緣(或右緣)相接了,同時這個元素會因為前一個浮動元素的存在,而寬度變窄。
MDN中給出的定義:Block formatting context
這裡給出的定義就是比較簡短的一句話,翻譯過來是:
塊格式化上下文(BFC)是網頁的可視化CSS渲染的一部分。它是塊盒子佈局的區域,也是浮動元素與其他元素交互的區域。
簡而言之,就是塊盒子內部的佈局區域,既然是內部,也就與外部相隔離開,並且在這個區域內,浮動元素會與其他元素產生交互。
觸發條件
由以上定義可知,容器滿足以下條件之一,其產生的包含塊便可以形成BFC:
- 為文檔的根元素(
<html>
) - 正常流中的塊級盒子
- 浮動元素
- 絕對定位元素
- 非塊盒子的塊容器(如inline-block、table-cells,以及table-captions)
overflow
不是visible
的塊盒子display
為flow-root
的元素(the display property)
除此之外,MDN還有其他補充(應該都屬於對非塊盒子的塊容器的補充):
- 表格單元格(
display
屬性為table-cell的元素,這是HTML表格單元格的預設設置) - 表格標題(
display
屬性為table-caption的元素,這是HTML表格標題的預設設置) - 由
display
為table、table-row、table-row-group、table-header-group、table-footer-group(分別是HTML表格、表格行、表格主體、表格頭部和表格底部的預設設置)或inline-table的元素隱式創建的匿名錶格單元格 overflow
屬性不是visible或clip的塊元素contain
屬性為layout、content或paint的元素- 彈性項(
display
屬性為flex或inline-flex的元素的直接子元素),如果它們本身並非flex或grid或table容器 - 網格項(
display
屬性為grid或inline-grid的元素的直接子元素),如果它們本身並非flex或grid或table容器 - 多列容器(
column-count
或column-width
屬性不為auto的元素,包括設置column-count:1
的元素) column-span:all
元素應始終創建新的格式化上下文,即使其不包含在多列容器中
佈局規則
同時根據以上的定義,還可以得出BFC內部的內容遵循以下佈局規則:
- BFC的內容包含在包含塊內
- 內部的盒子從包含塊的頂部開始一個接一個地垂直排列
- 兩個同胞盒子之間的垂直距離由'margin'屬性決定
- 相鄰塊級盒子之間的垂直'margin'會摺疊
- 每個盒子的左外緣與包含塊的左緣相接(對於自右向左的格式,則是右緣相接)。即使在浮動情況下也是如此(儘管盒子的行盒子可能因浮動而縮小),除非該盒子建立了新的塊格式化上下文(在這種情況下,盒子自身可能會因浮動而變窄)
同時BFC自身不會與同級的浮動元素髮生重疊
應用場景
至此,就大致瞭解了BFC是什麼,其觸發形成機制,以及其內部的佈局規則。
但通常情況下我們不會僅僅為了更改佈局去創建新的BFC,而是為瞭解決特定的問題來創建BFC,比如定位和清除浮動,因為建立了新BFC的容器將可以:
- 包含內部浮動元素(也就是浮動元素不會溢出容器之外)
- 排除外部浮動元素(利用BFC不會與同級浮動區域重疊的規則)
- 抑制margin摺疊(通過在BFC的內部創建新的BFC使得其中相鄰盒子的margin不發生摺疊)
以下就是MDN中的幾個例子:
包含內部浮動元素
<section>
<div class="box">
<div class="float">I am a floated box!</div>
<p>I am content inside the container.</p>
</div>
</section>
section {
height: 150px;
}
.box {
background-color: rgb(224, 206, 247);
border: 5px solid rebeccapurple;
}
.float {
float: left;
width: 200px;
height: 100px;
background-color: rgba(255, 255, 255, 0.5);
border: 1px solid black;
padding: 10px;
}
這其實也是一種清除浮動、解決高度坍塌的方案,在過去有些前端開發人員會給.box
這個div設置overflow: auto;
或者hidden
來使其內部形成BFC,這是一種方式,但這種解決方式存在一個問題,就是對於後續的開發者來說,他們可能不清楚為什麼要使用overflow
,如果要這麼用,最好用註釋說明清楚。
現在有一個新的display
值flow-root
可以讓我們用來創建新的BFC,用它會更好,因為從字面上更好理解,創建的容器為其內部的流式佈局創建新的上下文的行為類似於root
元素(也就是瀏覽器中的<html>
元素)。
排除外部浮動元素
這個例子中我們要實現雙列佈局,當然在現在CSS中,用彈性盒子實現更方便,但我們也可以通過BFC來實現。
在上一個例子中,可以看到浮動元素與後面的p元素重疊了,如果我們不想他們重疊,希望他們可以形成雙列佈局,就可以通過建立新的BFC來實現,比如給p元素套上一個div同時給其設置display: flow-root;
來手動觸發BFC的形成,因為BFC不會與任何同級浮動元素的margin盒子發生重疊。
<section>
<div class="float">Try to resize this outer float</div>
<div class="box"><p>Normal</p></div>
</section>
section {
height: 150px;
}
.box {
background-color: rgb(224, 206, 247);
border: 5px solid rebeccapurple;
}
.float {
float: left;
overflow: hidden; /* required by resize:both */
resize: both;
margin-right: 25px;
width: 200px;
height: 100px;
background-color: rgba(255, 255, 255, 0.75);
border: 1px solid black;
padding: 10px;
}
從而達到防止浮動元素與其他元素重疊。
阻止邊距摺疊
因為BFC內部的佈局規則是相鄰盒子間的垂直margin
會摺疊,如果我們不想起發生摺疊,也可以利用創建BFC的方式來達到目的。
<div class="blue"></div>
<div class="red"></div>
.blue,
.red {
height: 50px;
margin: 10px 0;
}
.blue {
background: blue;
}
.red {
background: red;
}
如果不想使紅色和藍色兩個元素的margin發生摺疊,就可以給紅色元素套上一個div並使其創建BFC,這個BFC就是一個獨立渲染的區域,這樣兩個元素的垂直margin就不會發生摺疊了。
總結
當我們使用Chrome的開發者工具,可以查看到網頁內的塊狀元素其對應的盒模型,margin、border、padding,以及最裡層的content,獨立的佈局渲染區域指的應該就是content這部分。
創建BFC的目的,就是想把容器內部的內容限定在這部分區域中,而不與外部元素產生作用,比如上個例子中阻止垂直margin
摺疊,就是把紅色元素限定在一個新的BFC中,從而不與外部的藍色元素髮生作用,這樣就不會出現margin
摺疊了。
參考資料
本文來自博客園,作者:beckyye,轉載請註明原文鏈接:https://www.cnblogs.com/beckyyyy/p/17668489.html