关于前端:对BFC的理解

38次阅读

共计 8157 个字符,预计需要花费 21 分钟才能阅读完成。

在前端的面试中,绝对 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 的块盒子
  • displayflow-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-countcolumn-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,如果要这么用,最好用正文阐明分明。

当初有一个新的 displayflow-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 折叠了。

参考资料

蕴含块(containing block)

边缘(edge)

W3C:Block formatting contexts

MDN:Block formatting context

正文完
 0