CSS朝花夕拾之块级格式上下文BFC

块级格式上下文(blocking format context,缩写为BFC)是个老生常谈的话题,前端面试时十有八九会问到。以前并不了解它,后来看了一些文章稍微理解了,但是随后就忘了,所以最近又重新看这个东西,把它记录在博客里加深记忆。先附上参考的资料,以表示对其作者的敬意。
1. 前端精选文摘:BFC 神奇背后的原理
2. 什么是BFC
3. CSS2 BFC模型和IFC模型
4. 格式化上下文( Formatting context )

这几篇文章很值得拜读,本文章也只是稍微总结一下,加深记忆。

一、 什么是BFC

首先给出W3C组织对BFC的定义,你也可以点击这查看原文

9.4 Normal flow
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.

9.4.1 Block formatting contexts

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).

我简要解释一下这段话的意思,解释不当之处还请指正:

第一段说明块级盒子参与块级格式上下文BFC。

第二段解释了哪些元素可以建立新的BFC,它们是浮动元素、绝对定位元素、本身不是块盒子的块级元素如inline-blocks、table-cells、table-captions,还有overflow属性值不为visible的块盒子。

第三段说明了BFC的布局:盒子在包含块的垂直方向上一个接一个放置,兄弟盒子的垂直距离由其margin属性决定。两个相邻盒子的垂直方向的margin值会折叠。

第四段讲在BFC中,每个盒子的左外边距挨着包含块的左边界(对于从右向左的格式来说)。即使在有浮动元素的存在下,这也是成立的,除非盒子建立了一个新的块级格式上下文(在有些情况下,盒子本身可能会因为浮动元素的存在而变窄)。

关于块级元素,这也有说明

Block-level elements are those elements of the source document that are formatted visually as blocks (e.g., paragraphs). The following values of the ‘display’ property make an element block-level: ‘block’, ‘list-item’, and ‘table’.

Block-level boxes are boxes that participate in a block formatting context. Each block-level element generates a principal block-level box that contains descendant boxes and generated content and is also the box involved in any positioning scheme. Some block-level elements may generate additional boxes in addition to the principal box: ‘list-item’ elements. These additional boxes are placed with respect to the principal box.

Except for table boxes, which are described in a later chapter, and replaced elements, a block-level box is also a block container box. A block container box either contains only block-level boxes or establishes an inline formatting context and thus contains only inline-level boxes. Not all block container boxes are block-level boxes: non-replaced inline blocks and non-replaced table cells are block containers but not block-level boxes. Block-level boxes that are also block containers are called block boxes.

块级元素是那些可视化为块的元素(如p元素),display值为block、list-item、table可以创建块级元素。块级元素参与块级格式上下文,每个块级元素生成一个包含后代盒子和生成内容的主要块级盒,且这个盒子参与了任何定位运算。一些盒子除了生成主要盒子外还可能生成其他盒子,如list-item元素。这些额外盒子关于主盒子排列。除了表格和可置换元素,块级盒子也是块容器盒子。块容器盒子要么只包含块级盒子要么建立行级格式上下文。并不是所有的块容器盒子都是块级盒子,如非置换行内块和非置换表单元格都是块容器却不是块级盒子。那些也是块容器的块级盒子被叫做块盒子。

由此可以得到:BFC(Block formatting context)是一个独立的渲染区域,只有Block-level Box参与, 它规定了内部的Block-level Box如何布局,并且与这个区域外部毫不相干。

  1. BFC布局规则
    • 内部的Box会在垂直方向,一个接一个地放置。
    • Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠
    • 每个元素的margin box的左边, 与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。
    • BFC的区域不会与float box重叠。
    • BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。
    • 计算BFC的高度时,浮动元素也参与计算
  2. 生成BFC的元素
    • 根元素
    • float属性不为none
    • position为absolute或fixed
    • display为inline-block, table-cell, table-caption, flex, inline-flex
    • overflow不为visible

二、 BFC的应用

  1. 两栏布局(左边固定宽度,右边自适应)

    <div class="container">
      <aside class="aside"></aside>
      <main class="main"></main>
    </div>
    *{
      padding:0;
      margin:0;
    }
    .aside{
      width:200px;
      height:400px;
      background-color:#fefe88;
      float:left;
    }
    .main{
      height:500px;
      background-color:#88fefe;
    }

    示意图
    图中可以看出.main和.aside区域重叠了,按照浮动元素的布局来说,.main的文字会围绕浮动元素,我们想要两栏布局,而这不是我们想要的布局。为什么会这样呢?这是因为BFC布局规则的第三条:每个元素的margin box的左边, 与包含块border box的左边相接触,即使存在浮动也是如此。所以.main左区域会紧挨.container的左侧。
    为此我们可以利用布局规则的第四条:BFC的区域不会与float box重叠。为.main新建一个BFC。可以为做如下设置:

    .main{
      height:500px;
      background-color:#88fefe;
      overflow:auto;
    }

    得到如下所示:
    为.main新建bfc

  2. 解决浮动导致的父元素坍塌
    考虑如下布局:

    <div class="container">
      <div class="left"></div>
      <div class="right"></div>
    </div>
    *{
      padding:0;
      margin:0;
    }
    .left{
      width:200px;
      height:400px;
      background-color:#fefe88;
      float:left;
    }
    .right{
      width:200px;
      height:400px;
      background-color:#88fefe;
      float:left;
    }

    我们知道父元素如果仅包含浮动元素,且没有设置宽高时,其宽会为窗口的宽度,高度为0,这也就是父元素高度坍塌。其原因是浮动元素脱离了标准流。上个例子中虽然.aside脱离文档流,但.main没有脱离,所以.container还是有高度的。那怎么解决呢?可以利用布局规则第六条:计算BFC的高度时,浮动元素也参与计算。所以可以将.container变为bfc,同样可以采用overflow属性。

    .container{overflow:auto;}
  3. 解决外边距折叠
    试看如下代码:
<div class="test">设置下外边距30px</div>
<div class="test">设置上外边距30px</div>
.test{
      width:200px;
      height:100px;
      background-color:#fe88fe;
      margin:50px 0;
    }

布局图如下所示:
示意图
从上图可以看出两个块的外边距发生了折叠,原本我们希望两个块间隔达到50px+50px的外边距,却得到了只有50px的间隔。为什么呢?布局规则第二条:- Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠。注意是同一个BFC的两个相邻box margin发生重叠。你可能会问就上面的两行html,没看出哪建立了bfc呀!原因是他们都处于根元素下,而根元素会产生BFC。解决方法可以根据布局规则第五条:BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。所以可以为二者其中一个建立一个新的bfc,因为bfc是隔离独立的,不会影响外面的布局,外面也不会影响它自己的布局。就不会发生外边距折叠问题了。如下所示:

<div class="test">设置下外边距50px</div>
<div class="wrap">
  <div class="test">设置上外边距50px</div>
</div>
.test{
  width:200px;
  height:100px;
  background-color:#fe88fe;
  margin:50px 0;
}
.wrap{
  overflow:hidden;
}

外边距折叠问题解决了
好了,这样问题就解决了。
其实写这篇文章挺心虚的。一方面我并没有仔细对W3C文档做深入理解,另一方面只是对看过的几篇文章做个简单地总结,设置有些还是照搬原文的。而自己的东西还是少之又少的。就这样吧!
如您发现错误,请不吝指正。

发布了21 篇原创文章 · 获赞 27 · 访问量 8万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章