margin collapse & BFC2021-12-06

外边距重叠

外边距重叠(英文margin collapse,MDN 的解释如下:

块的顶部和底部边距有时合并(折叠)为单个边距,其大小是单个边距(或者是单个边距中最大的一个,如果它们相等的话),这种行为称为边距折叠。注意,浮动和绝对定位元素的边距永远不会崩溃。

这种情况并不是浏览器的bug,而是为某些情景而设计的,如下:

<section class="design">
  <p>
    Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam harum
    volutes ulema, quia venial sung in maiores option laboriously, harum
    minus veritas destruct selectus repellent's eacute nostrum vitae! Amet,
    alias.
  </p>
  <p>
    Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam harum
    volutes ulema, quia venial sung in maiores option laboriously, harum
    minus veritas destruct selectus repellent's eacute nostrum vitae! Amet,
    alias.
  </p>
  <p>
    Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam harum
    volutes ulema, quia venial sung in maiores option laboriously, harum
    minus veritas destruct selectus repellent's eacute nostrum vitae! Amet,
    alias.
  </p>
  <p>
    Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam harum
    volutes ulema, quia venial sung in maiores option laboriously, harum
    minus veritas destruct selectus repellent's eacute nostrum vitae! Amet,
    alias.
  </p>
</section>

YmdDgF

为了让段落分明,一般会给上margin

p{
  text-indent: 2em;
  margin: 1.7em 0;
}

hlUKiw

当你打开控制台查看p标签的时候,会发现他们间隔是一样的,也就是为了这种情况而设计的,不过也产生了一种生产环境经常遇到的「bug」,叫margin collapse,也就是外边距重叠了

005UbvFEgy1h98io5mqkeg30go0b6to2

外边距重叠的三种情况

  1. 相邻的两个块级盒子,在第一个盒子设置了margin-bottom后第二个盒子设置了margin-top
  2. 三个层级的块盒子,最后一个层级的盒子相对于第二个层级盒子设置margin-top
  3. 三个同级对块盒子,中间对盒子内容为空,想设置margin-top和margin-bottom级来为上下两个盒子制造空隙,将发生外边距崩塌

💡 注意,会造成外边距重叠的情况,只有垂直方向上的块级盒子

NsylL4

解决方案

  1. 子绝父相
  2. inline-block不会造成外边距崩塌,所以可以改成inline-block(改成inline-block后要记得设置宽高)
  3. 设置position为relative,然后用top来代替margin-top
  4. 设置float后再来margin-top
  5. 不能设置外边距,那就改成内边距
  6. 添加边框,设置边框厚度
  7. 创建BFC(overflow:hidden会触发创建BFC)

BFC(block formating context 块级格式化上下文)

前端的布局有三种:

  1. 普通流(也是浏览器默认的)
    • 块级格式化上下文(BFC,block formatting context)。
    • 内联格式化上下文(IFC,inline formatting context)。
    • 弹性格式化上下文(FFC,flex formatting context),在 CSS3 中定义。
    • 栅格格式化上下文(GFC,grid formatting context),在 CSS3 中定义。
  2. flow布局
  3. 绝对定位

后两个都会脱离文档流,接下来要讲的BFC,就是属于普通流一种

是的:BFC会形成独立的渲染区域⚠️,使得内部元素的渲染不会影响到外界

  1. 清除浮动

     //主要代码,为了方便看到效果,给了背景色
     // rgb(228, 99, 48)
     <div class="content_box">   
        <img src="./img/selfie.jpg"/>
        // rgb(168, 218, 235)
        <div class="statement">
           <p>...</p>
           <p>...</p>
        </div>
     </div>
    ![goI4GD](https://oshino.oss-cn-shenzhen.aliyuncs.com/uPic/goI4GD.png)

    一般在情况下,都是一边产品图,一边文字说明,这里我们让图片设置为 flow(当然还有其他解决方案,这里是为了方便举例子,其他就不扩展了)

    O6Gg09

    可以看到flow设置为flow之后,img脱离了文档流,content_box的大小变成只靠两个p来支撑,我们需要的是content_box是img的大小也影响着content_box,这种情况下就可以给content_box的css添加一个属性来触发BFC,因为为了在此情况下不要有太大改变,一般我们使用display:flow-root或者contain:layout(加黑是因为你也可以使用其他属性来触发BFC,比如overflow:hidden)

     .content_box {
           display: flow-root;
     }
    ![ogo3A0](https://oshino.oss-cn-shenzhen.aliyuncs.com/uPic/ogo3A0.png)

    这样就可以清除掉img的 flow:left 所脱离文档流从而影响content_box的整体渲染

  2. 包裹浮动

    还是上面👆的例子,打开控制台,元素里查看以下statement盒子

    LfbMiS

    看到这里的p标签还是占着一整行,这样在我们后面调整statement盒子使得文字不要那么贴近图片的时候是很不方便的,比如添加padding-left(ps:调整p的padding/margin也一样)

    ElYO1F

    我们也可以给statement添加一个BFC,让statement成为一个独立渲染的区域不影响外部

     .statement{
      padding-left: 2em;
      display: flow-root;
     }
    再刷新一下

    QdvTVP

    这样就可以使元素(statement)包裹着浮动元素了

  3. 解决margin collapse

    还是上面的例子,p标签太近了,我们添加个属性margin

     p{
      margin: 1.7em 0;
     }
    ![005UbvFEgy1h98iruk3dhg30go08pttp](https://oshino.oss-cn-shenzhen.aliyuncs.com/uPic/005UbvFEgy1h98iruk3dhg30go08pttp.gif)

    可以看到这里发生了一开始我们提到的外边距重叠(margin collapse),我也提到了这是专门这样子设计的,不是bug,怎么解决呢,没错,上面解决方案的第七个,可以创建一个BFC

     <div class="statement">
       <div>
         <p>
           Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam harum
           volutes ulema, quia venial sung in maiores option laboriously, harum
           minus veritas destruct selectus repellent's eacute nostrum vitae! Amet,
           alias.
         </p>
       </div>
       <div>
         <p>
           Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam harum
           volutes ulema, quia venial sung in maiores option laboriously, harum
           minus veritas destruct selectus repellent's eacute nostrum vitae! Amet,
           alias.
         </p>
       </div>
     </div>
     .statement div{
       display: flow-root;
     }
    给p套一个div,让这个div形成一个BFC独立渲染

    L2jdj7

    这样就可以防止外边距重叠了

创建BFC有以下(用得比较多得加黑了

  1. <html/>根元素
  2. 设置了float属性且值不为none的元素
  3. position:absolute、fixed的元素
  4. 设置了display:inline-block/ flow-root的元素
  5. 设置了display:table-的元素(例如table-cell、table-caption、table-row*等)
  6. 设置了overflow值不为visible和clip以外的元素(例如:overflow:hidden)
  7. 设置contain的元素(layout | content | paint)
  8. flex 和grid布局的子元素(非flex和grid布局容器本身)
  9. 多列布局,设置了column-count的元素,或设置了column-span:all的元素

    code inline.

提示display: flow-rootcontain: layout 等是无副作用的,可在不影响已有布局的情况下触发 BFC,所以会用的比较多 a