BFC介绍及其作用

914 阅读11分钟

1. 什么是margin塌陷?

举个例子,像下面这种情况,上下两个盒子的间距按说是20px和40px,但实际都是20px,这就是塌陷。

      <div style="background-color:tomato;width:100px;height:100px;margin-bottom:20px;"> hello world</div>
      <div style="background-color:#bbb;width:100px;height:100px;margin-top:0px;"> hello world</div>
      <hr>
      <div style="background-color:tomato;width:100px;height:100px;margin-bottom:20px;"> hello world</div>
      <div style="background-color:#bbb;width:100px;height:100px;margin-top:20px;"> hello world</div>

其实这是CSS1.0故意设计的,因为这样符合平面设计师的要求。

1.1. 为什么只在垂直方向塌陷

在CSS出现之前使用的p标签,它要求每个段落之间有相同的间隔,所以当你对p标签设置了margin-topmargin-bottom你发现就会出现塌陷的情况,而且会取二者之中的最大值。

明白塌陷产生的条件也就明白了如何解决塌陷所带来的问题,那么塌陷产生的条件是什么?

首先是一个大前提:元素之间没有被非空内容、padding、border 或 clear 分隔开。然后有符合下面几种毗邻情况:

top margin of a box and top margin of its first in-flow child
(一个元素的 margin-top 和它的第一个子元素的 margin-top)

bottom margin of box and top margin of its next in-flow following sibling
(普通流中一个元素的 margtin-bottom 和它的紧邻的兄弟元素的的 margin-top)

bottom margin of a last in-flow child and bottom margin of its parent if the parent has ‘auto’ computed height
(一个元素( height 为 auto )的 margin-bottom 和它的最后一个子元素的margin-bottom)

top and bottom margins of a box that does not establish a new block formatting context and that has zero computed ‘min-height’, zero or ‘auto’ computed ‘height’, and no in-flow children
(一个没有创建 BFC、没有子元素、height 为0的元素自身的 margin-top 和 margin-bottom)

W3C规范这样描述:

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浮动,盒子本身将会变得更窄)。

1.2. 解决方案

方法已在代码中给出

    <div class="wrapper">
        <div class="content">
        </div>
    </div>
    * {
        margin: 0;
        padding: 0;
    }

    .wrapper {
        background-color: #000;
        margin-left: 100px;
        margin-top: 100px;
        width: 200px;
        height: 200px;
        /* float: left; */
        /* position: absolute; */
        /* display: inline-block; */
        /* display: flex; */
        /* 注意使用 overflow 溢出的部分会自动隐藏起来*/
        /* overflow: hidden; */
        /* 注意使用display: table虽然可以解决塌陷,但是cotainer的高度会失效 */
        /* display: table; */
        /* overflow: scroll; */
        /* overflow: scroll 溢出的部分会变成滚动,效果奇葩,应该没什么用 */
    }

    .content {
        /* 设置 margin-top: 150px;我们发现子级盒子会带着父级盒子一起往下移动,好像二者粘连到了一起或者说子级盒子好像没有了“顶”,此时会取wrapper和content的一个margin-top最大值。这种现象叫做margin塌陷,是经典BUG。 */
        margin-top: 150px;


        /* 在子元素中使用以下这些也可以解决塌陷 */
        /* float: left; */
        /* position: absolute; */
        /* display: inline-block; */
        width: 100px;
        height: 100px;
        background-color: #0f0;
        margin-left: 50px;
    }

2. margin合并(也叫margin折叠)

2.1. 参考

2.2. 触发原因

  1. 相邻的两个元素之间的外边距重叠。
  2. 没有内容将父元素和后代元素分开。
  3. 空的块级元素。
  • 例子1
      <div class="wrapper"> margin-top=height=margin-bottom: 50px;</div>
      <div class="content"> height=margin-top: 50px; 按理说二者之间的间距该是50+50px,但合并之后只有50</div>
      .wrapper {
         background-color: rgb(68, 29, 29);
         margin-top: 50px;
         height: 50px;
         margin-bottom: 50px;
      }

      .content {
         height: 50px;
         margin-top: 50px;
         background-color: #0f0;
      }
  • 例子2
      <div class="contain">
         <div class="context">dede</div>
      </div>
      .contain {
         height: 200px;
         margin-top: 10px;
         background-color: red;
      }

      .context {
         height: 100px;
         margin-top: 100px;
         background-color: blue;
      }
  • 例子3
      <div class="bro">height: 20px</div>
      <div class="demo"></div>
      <div class="bro">height: 20px</div>
      .bro {
         height: 20px;
         background-color: aqua;
      }

      .demo {
         margin-top: 20px;
         margin-bottom: 40px;
      }

2.3. 解决方法

从触发原因出发,分情况讨论。

假如是原因1导致的重叠,则可以创建不同的BFC,分别包起来。

这一开始听起来可能有些困惑,因为 BFC 导致其相邻子元素外边距折叠的问题。但我们必须牢记的是毗邻块盒子的垂直外边距折叠只有他们是在同一 BFC 时才会发生。如果他们属于不同的BFC,他们之间的外边距将不会折叠。所以通过创建一个新的 BFC 把原先有 margin 合并问题的包起来可以防止外边距折叠。

如下,之后给父级元素添加overflow: hidden; (出发BFC的一种方式)即可解决margin合并

      <div class="container">
         <div class="content1"></div>
         <div class="content2"></div>
      </div>
      <div class="container">
         <div class="content3"></div>
      </div>

效果如下

上面一段话的原文链接: www.w3cplus.com/css/underst…

假如是原因2导致的折叠,则可以在父子元素之间加入内容即可。

  • 给父元素添加border、padding。
  • 父子元素之间添加行内内容。
  • 用BFC把子元素包起来,这时原先子元素的布局和原先父元素就没关系了。

假如是原因3导致的折叠,则可以在中间的块元素加上内容即可。加上padding、border、height、min-height皆可。

3. 浮动

3.1. 浮动实例1

3.2. 浮动实例2

浮动元素带有float的元素,最早用来实现类似于报纸图片环绕的布局

      <div class="wrapper">
         <div class="content">1</div>
         <div class="content">2</div>
         <div class="content">3</div>
         <div class="content">4</div>
         <div class="content">5</div>
         <div class="content">6</div>
         <div class="content">7</div>
         <div class="content">8</div>
         <div class="content">9</div>
      </div>
      <br>
      <div class="context1">YAMA</div>
      <div class="context2">YAMA</div>
      * {
         margin: 0;
         padding: 0;
      }

      .wrapper {
         background-color: aqua;
         /* width: 330px; */
         border: 2px solid black;
         /* 解决方法如下,在父级盒子添加overflow: hidden; */
         overflow: hidden;
      }

      .content {
         float: left;
         /*使元素站队、浮动*/
         color: #fff;
         margin-left: 10px;
         margin-bottom: 10px;
         background-color: black;
         width: 100px;
         height: 100px;
      }

      .context1 {
         float: left;
         width: 100px;
         height: 100px;
         background-color: red;
         color: white;
         opacity: 0.7;
      }

      .context2 {
         width: 150px;
         height: 150px;
         background-color: yellow;
         color: blue;
         /* 解决方法如下,在后面的元素变为行内块 */
         /* display: inline-block; */
         /* float: left; */
         /* float: right; */
      }

效果如图

此处我们注意到后面两个div好像具有层模型的特点,实际上不是,正确理解为:

浮动元素产生了浮动流,块级元素看不到所有产生了浮动流的元素。看不到指的是在进行定位时会无视它而进行覆盖,就像层模型一样绝对定位。

产生了BFC的元素、带有inline属性的元素以及浮动元素都能看到浮动元素,比如a标签会围绕浮动元素进行布局。

后面句话我也搞不清楚怎么写出来的了,年代久远。(还需注意,如果设置position和float后由内容决定大小,打内部把元素转换成inline-block)

3.3. 浮动实例3

淘宝网

4. CSS定位机制

参考:

CSS 有三种定位机制,常规流(static、relative)、定位流(absolute、fixed)、浮动流(float)

4.1. 常规流

也可叫普通流,没有改变默认布局规则情况下的页面元素布局方式,元素一个接一个排列,BFC竖着排,IFC横着排。

当position为static、relative且没有浮动时就是常规流。

4.2. 定位流

position为absolute、fixed时元素会从常规流中移除,所以它不会影响常规流布局,在常规流中的位置也不会保留。absolute相对于最近的可定位(position为relative、fixed、absolute之一)的父级元素进行定位。fixed以窗口进行定位。

4.3. 浮动流

float为left、right。常规流的inline元素(比如a)围绕浮动元素进行布局。block元素不受影响,上面讲过,块级元素看不到所有产生了浮动流的元素。看不到指的是在进行定位时会无视它而进行覆盖,就像层模型一样绝对定位,但并不是层模型。

4.3.1. 实例

      <div class="wrapper">
         <div class="content">1</div>
         <div class="content">2</div>
         <div class="content">3</div>
         <p>YAMA</p>
      </div>
      * {
         margin: 0;
         padding: 0;
      }

      .wrapper {
         border: 10px solid black;
         /* overflow: hidden; */
      }

      .content {
         float: left;
         color: #fff;
         margin: 2px;
         background-color: black;
         width: 100px;
         height: 100px;
      }

      p {
         background-color: cadetblue;
         /* clear: both; */
      }

下图中上面的图片设置了overflow: hidden;,下面的没有

  • p标签属于块级元素会忽视float,在进行定位假设之前的浮动元素不存在(但是注意这里和之前的层模型区分开来)
  • 下面的图中的下面那部分是p标签添加clear: both;之后,意义是不允许该标签附近出现浮动对象,(相应的值还有左右),相当于清除了div标签的浮动特性。

5. BFC简介

块格式化上下文(Block Formatting Context,BFC) 是Web页面的可视CSS渲染的一部分,是块盒子的布局过程发生的区域,也是浮动元素与其他元素交互的区域。

参考:

5.1. 如何创建BFC

完整参考:mdn 块格式化上下文

常见总结

  1. 根元素(整个页面就是一个大的BFC);
  2. float为 left | right;
  3. overflow为 hidden | auto | scroll;
  4. display为 inline-block | table-cell | table-caption | flex | inline-flex;
  5. position为 absolute | fixed;

请注意,BFC并不是一个css属性,也不是一段代码,而是css中基于box的一个布局对象,它是页面中的一块渲染区域,并且有一套渲染规则,它决定了其子元素将如何定位,以及和其他元素的关系和相互作用。明确地,它是一个独立的盒子,并且这个独立的盒子内部布局不受外界影响,当然,BFC也不会影响到外面的元素

5.2. BFC作用

5.2.1. 使用BFC来防止文字环绕

有时候一个浮动div周围的文字环绕着它(如下图中的左图所示)但是在某些案例中这并不是可取的,我们想要的是外观跟下图中的右图一样的。为了解决这个问题,我们可能使用外边距,但是我们也可以使用一个BFC来解决。

      <div class="container">
      <div class="floated">
         <img width="150px" src="https://ythdong.gitee.io/blog_image/JavaScript/%E6%8D%95%E8%8E%B720.PNG">
      </div>
      <p>YAMA YAMA YAMA YAMAYAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMAYAMA YAMA.
         YAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMAYAMA YAMA
         YAMA YAMA
         YAMA YAMA YAMA YAMAYAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMAYAMA YAMA.
         YAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMA YAMAYAMA YAMA
         YAMA YAMA.</p>
     </div>
    .floated {
        float: left;
        margin: 5px;
    }

    p {
        color: rgb(14, 13, 13);
        text-align: center;
        /* 解决方法如下 */
        overflow: hidden;

        /* 以下两种亦可,但效果不好 */
        /* display: inline-block; */
        /* clear: both; */
    }

5.2.2. 在多列布局中使用BFC

如果我们正在创建的一个多列布局占满了整个容器的宽度,在某些浏览器中最后一列有时候将会被挤到下一行。会发生这样可能是因为浏览器舍入(取整)了列的宽度使得总和的宽度超过了容器的宽度。然而,如果我们在一个列的布局中建立了一个新的BFC,它将会在前一列填充完之后的后面占据所剩余的空间。

      <div class="container">
         <div class="column">column 1 </div>
         <div class="column">column 2 </div>
         <div class="column">column 3 </div>
      </div>
      .column {
         width: 32vw;
         height: 800px;
         background-color: green;
         float: left;
         margin: 0 0%;
         margin: 1px;
         text-align: center;
      }

      .column:last-child {
         float: none;
         overflow: hidden;
      }

现在即使容器的宽度会有轻微的变化,但是布局也不会中断。当然,这并不是多列布局的最好选择,但它是防止最后一列下滑问题的一种方法。Flexbox在这种情况下可能是一个更好的解决方案,但是这应该要说明一下在这些情况下元素是如何表现的。

6. 两栏布局

实现两栏布局

      <div class="container">
         <div class="aside">
               aside
         </div>
         <div class="main">
               main content
         </div>
      </div>
      .container {
         border: 10px solid rgb(106, 48, 214);
         width: 90vw;
         height: 95vh;
         margin: 0 auto;
         overflow: hidden;
      }

      .aside {
         text-align: center;
         height: 93vh;
         background: #837979;
         width: 100px;
         margin: 10px;
         float: left;
      }

      .main {
         text-align: center;
         background: #77a5cf;
         overflow: hidden;
         height: 93vh;
         margin: 10px;
      }

7. IFC、GFC、FFC

segmentfault.com/a/119000001…