外边距重叠现象

CSS 外边距重叠的发生条件与解决方法

问题

什么是外边距重叠(Margin Collapse)?什么情况下会发生?如何避免?

解答

外边距重叠是指两个或多个垂直方向的外边距相遇时,会合并成一个外边距,取其中较大的值。

发生的三种情况

1. 相邻兄弟元素

<style>
  .box1 {
    margin-bottom: 30px;
    background: lightblue;
  }
  .box2 {
    margin-top: 20px;
    background: lightgreen;
  }
</style>

<div class="box1">Box 1</div>
<div class="box2">Box 2</div>

<!-- 两个盒子之间的间距是 30px,不是 50px -->

2. 父子元素

<style>
  .parent {
    background: lightblue;
    /* 父元素没有 border、padding 或 BFC */
  }
  .child {
    margin-top: 20px;
    background: lightgreen;
  }
</style>

<div class="parent">
  <div class="child">Child</div>
</div>

<!-- 子元素的 margin-top 会"穿透"到父元素外部 -->

3. 空块元素

<style>
  .empty {
    margin-top: 20px;
    margin-bottom: 30px;
    /* 没有内容、padding、border、height */
  }
</style>

<div class="empty"></div>

<!-- 自身的上下外边距会重叠,最终只有 30px -->

解决方法

方法一:创建 BFC

.parent {
  overflow: hidden; /* 或 auto */
}

/* 或者 */
.parent {
  display: flow-root; /* 推荐,专门用于创建 BFC */
}

方法二:添加边框或内边距

.parent {
  padding-top: 1px; /* 或 border-top: 1px solid transparent */
}

方法三:使用 Flexbox 或 Grid

.parent {
  display: flex;
  flex-direction: column;
}

完整示例

<!DOCTYPE html>
<html>
<head>
  <style>
    /* 问题:父子外边距重叠 */
    .problem .parent {
      background: lightblue;
    }
    .problem .child {
      margin-top: 30px;
      background: lightgreen;
    }

    /* 解决:使用 t0dtt 创建 BFC */
    .solution .parent {
      background: lightblue;
      display: flow-root;
    }
    .solution .child {
      margin-top: 30px;
      background: lightgreen;
    }
  </style>
</head>
<body>
  <h3>问题:margin 穿透</h3>
  <div class="problem">
    <div class="parent">
      <div class="child">Child</div>
    </div>
  </div>

  <h3>解决:BFC 阻止穿透</h3>
  <div class="solution">
    <div class="parent">
      <div class="child">Child</div>
    </div>
  </div>
</body>
</html>

关键点

  • 只发生在垂直方向,水平方向不会重叠
  • 重叠后取较大值(都为正),都为负取绝对值较大的,一正一负则相加
  • 创建 BFC 可以阻止父子元素的外边距重叠
  • display: flow-root 是创建 BFC 的最佳方式
  • Flexbox 和 Grid 布局中不存在外边距重叠