CSS | BFC 外边距重叠 高度塌陷

前言:外边距重叠与高度塌陷是在写前端页面的时候经常碰到的一个问题,所以我想通过这篇博客记录下这块的知识点,方便后续查阅。

问题1:外边距重叠(外边距合并)

首先来看下MDN给出的定义:

块级元素的上外边距 (margin-top)下外边距 (margin-bottom)有时合并 (或者折叠) 为单个外边距,其大小取其中的最大者(如果相等,则取其中一个),这种行为称为边距折叠(margin collapsing)。

备注: 浮动元素和绝对定位元素的外边距不会折叠。

那么对应的有3种情况:

  1. 相邻元素之间
  2. 父元素和后代元素之间
  3. 空的块级元素

下面依次来看看这些情况。

相邻元素之间

代码:

HTML

1
2
<div class="up">我是上面的盒子</div>
<div class="down">我是下面的盒</div>

CSS

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
* {
  padding: 0;
  margin: 0;
}

.up {
  width: 150px;
  height: 50px;
  margin-bottom: 100px;
  background: red;
}

.down {
  width: 150px;
  height: 50px;
  margin-top: 50px;
  background: blue;
}

image-20221228225501673

可以看到上下两个元素本来应该是有150px的距离,但是发生了外边距重叠,只会挑选最大的边距的值,结果只有100px

父元素和后代元素

代码:

HTML

1
2
3
4
5
<section>
    <header>上边界重叠 87</header>
    <main></main>
    <footer>下边界重叠 87 不能再高了</footer>
</section>

CSS

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
section    {
  width: 350px;
  margin-top: 13px;
  margin-bottom: 87px;
  background-color: orange;
  color: #fff;
}

header {
  margin-top: 87px;
  width: 200px;
  background-color: red;
}

footer {
  width: 200px;
  margin-bottom: 13px;
  background-color: blue;
}

image-20221228231642528◎ 可以看到上下边距都为87px

在预想的情况下,父级盒子也就是section元素的上下边距应该分别为13px以及87px,但是发生了外边距重叠,父块元素和其内后代块元素外边界重叠,重叠部分最终会溢出到父级块元素外面,导致上下的外边距都为87px

空的块级元素

代码:

HTML

1
2
3
<p>上边界范围是 87 ...</p>
<div></div>
<p>... 上边界范围是 87</p>

CSS

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
p {
  margin: 0;
  background-color: blue;
  color: #fff;
}
div {
  margin-top: 13px;
  margin-bottom: 87px;
  background-color: red;
}

image-20221228232756915

在预想的情况下,中间的div盒子应该将两个p标签隔开100px的距离,由于空元素的margin-top直接贴到元素下边界的margin-bottom,导致发生了外边距重叠。

问题2:高度塌陷

在浮动布局中,父元素默认是被子元素撑开的 当子元素浮动后,其会完全脱离文档流,子元素从文档流中脱离 将会无法撑起父元素的高度,导致父元素的高度丢失 父元素高度丢失后,其下的元素会自动上移,导致页面布局混乱。

比如现在有这样一个塌陷的案例:

代码:

HTML

1
2
3
4
5
<div class="parent">
 <!-- 父亲颜色是红色 -->
 <div class="child">SON</div>
 <!-- 儿子的颜色是蓝色 -->
</div>

CSS

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
/*css代码*/
div.parent{
 width: 200px;
 background: red;
}
div.child{
 width: 50px;
 height: 50px;
 background: blue;
 float: left;
}

因为父元素没有设置大小(或不设置高度)子元素浮动,子元素会跳出父元素的边界(脱离标准流)当父元素的高度为auto时,父元素的高度直接为0(父盒子将没有内容,不再有高度)。

BFC

首先来看看MDN官方的定义:

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

理解:具有BFC特性的元素可以看作是一个隔离的独立的容器,容器里面的元素不会在布局上影响外面的元素,BFC具有普通容器所没有的特性。

BFC的作用

  1. 自适应两栏布局
  2. 阻止元素被浮动覆盖
  3. 清除内部浮动
  4. 防止外边距塌陷
  5. 解决高度塌陷问题

开启BFC的方法

  1. 根元素(<html>
  2. 浮动元素(float 值不为 none
  3. 绝对定位元素(position 值为 absolutefixed
  4. 行内块元素(display 值为 inline-block
  5. 表格单元格(display 值为 table-cell,HTML 表格单元格默认值)
  6. overflow 值不为 visibleclip 的块元素
  7. display 值为 flow-root 的元素

所以就可以通过BFC的方式来解决上面遇到的问题1与问题2,现在就来看看怎么解决吧!

解决方法

外边距重叠

相邻元素之间

比如上面提到的 相邻元素之间 的情况,可以使用将两个div分别放入两个不同的容器中,设置容器的overflow值不为 visibleclip ,使其变为BFC,这样updown两个盒子就置于不同的BFC中了,从而解决了外边距重叠。

代码:

HTML

1
2
3
4
5
6
<div class="container">
  <div class="up">我是上面的盒子</div>  
</div>
<div class="container">
  <div class="down">我是下面的盒</div>
</div>

CSS

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
.container{
  overflow: hidden;
}

.up {
  width: 150px;
  height: 50px;
  margin-bottom: 100px;
  background: red;
}

.down {
  width: 150px;
  height: 50px;
  margin-top: 50px;
  background: blue;
}

image-20221229211054790◎ 可以看到现在上下的外边距不会重叠了

父元素和后代元素

同样的可以利用刚才解决 相邻元素之间 情况的思路,将父元素设置为BFC(即设置overflow),此时父元素与子元素置于不同的BFC,就可以解决高度重叠的问题了 代码:

HTML

1
2
3
4
5
<section>
    <header>上边界重叠 87</header>
    <main></main>
    <footer>下边界重叠 87 不能再高了</footer>
</section>

CSS

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
section {
  width: 350px;
  margin-top: 13px;
  margin-bottom: 87px;
  background-color: orange;
  color: #fff;
  overflow: hidden;
}

header {
  margin-top: 87px;
  width: 200px;
  background-color: red;
}

footer {
  width: 200px;
  margin-bottom: 13px;
  background-color: blue;
}

image-20221229212036769

image-20221229212101574

image-20221229212113853

高度塌陷

对于问题2的高度塌陷,解决的思路相对会多一些,请参考以下的几种解决思路:

  1. 将盒子的大小写死

    缺点:非自适应。

  2. 在高度塌陷那层的盒子上设置,overflow使其变为BFC

    1
    2
    3
    
    overflow:hidden;
    /*或者*/
    overflow:auto;
    

    缺点:

    • auto 会出现滚动条,影响美观
    • hidden 会带来内容不可见的问题。
  3. 给外部的父盒子也添加浮动,也让其脱离标准流

    缺点:不易维护

  4. 给父盒子末尾添加一个空盒子,并设置成清除浮动

    代码:

    HTML

    1
    2
    3
    4
    5
    6
    
    <div class="parent">
        <!-- 父亲颜色是红色 -->
        <div class="child">SON</div>
        <!-- 儿子的颜色是蓝色 -->
        <br style="clear:both" />
    </div>
    

    缺点:引入了不必要的冗余元素

  5. after伪元素清除浮动(推荐使用

    • 给外部盒子的after伪元素设置clear属性,再隐藏它。

    • 这是一种纯CSS的解决浮动造成盒子塌陷方法,没有引入任何冗余元素,推荐使用此方法来解决CSS盒子塌陷。

    代码:

    HTML

    1
    2
    3
    4
    5
    
    <div class="parent">
        <!-- 父亲颜色是红色 -->
        <div class="child">SON</div>
        <!-- 儿子的颜色是蓝色 -->
    </div>
    

    CSS

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    
    /*css代码*/
    div.parent{
      width: 200px;
      background: red;
    }
    div.child{
      width: 50px;
      height: 50px;
      background: blue;
      color: #fff;
      float: left;
    }
    
    div.parent:after{
        content: "";
        display: block;
        height: 0;
        clear: both;
        overflow: hidden;
        visibility: hidden;
       /*为了保险起见把能写上的都写上叭*/
    }
    

    缺点:低版本IE不兼容。

参考文献