妈妈再也不用担心我不会外边距折叠了

简介

相邻的常规流块盒的上外边距(margin-top)和下外边距(margin-bottom)有时合并(折叠)为单个边距,这种行为称为外边距折叠

这段文字看起来可能有些抽象,话不多说,我们先举个例子:

现在有两个垂直方向排列的相邻块级元素,element1margin-bottom属性值为20pxelement2margin-top属性值为50px,那么最终两者呈现出来间隔是多少?

<style>
  div {
    width: 400px;
    height: 400px;
  }

  .element1 {
    margin-bottom: 20px;
    background-color: #000;
  }

  .element2 {
    margin-top: 50px;
    background-color: red;
  }
</style>

<body>
  <div class="element1"></div>
  <div class="element2"></div>
</body>
复制代码

没有了解过此类问题的同学可能张口就来一个70px,我之前也是那么认为的哈哈,但实际结果确是50px;从页面中可以看到element1确实有一个20px的外边距,element2也确实有个50px的外边距,但是这两个元素之间的距离却不是20px+50px,而是取了其中最大的外边距50px作为间隔距离,这种情况就称之为外边距折叠

外边距折叠触发的条件

同一层级的两个相邻元素

常规流块盒若外边距无缝相邻(没有borderpadding阻隔),则会进行外边距合并,我们刚刚举的例子就是这种情况,那么这种情况下要怎样才能避免外边距折叠所带来的影响呢?

  1. 为其中一个元素外层套上一个父元素,并为这个父元素创建BFC

创建BFC的方式有很多,在这里我就用了一种比较简单、副作用较小的overflow: hidden属性创建BFC;因为创建了BFC的元素不会与其子元素发生外边距折叠,恰好可以解决这个问题;不了解BFC的朋友可以看我这篇讲解BFC的文章:juejin.cn/post/709965…

<style>
  .father {
    overflow: hidden;
  }
  
  .element1,
  .element2 {
    width: 200px;
    height: 200px;
  }

  .element1 {
    margin-bottom: 20px;
    background-color: #000;
  }

  .element2 {
    margin-top: 50px;
    background-color: red;
  }
</style>

<body>
  <!-- <div class="father">
    <div class="element1"></div>
  </div>
  <div class="element2"></div> -->

  <div class="element1"></div>
  <div class="father">
    <div class="element2"></div>
  </div>
</body>
复制代码

  1. 让处在下方的元素浮动

外边距折叠是发生在常规流块盒中的布局方式,而浮动元素已经脱离了文档流,自然也就不会和上面的常规流块盒发生外边距折叠了;但不要给处在上方的元素添加,否则会出现浮动元素与常规流块盒的重叠问题

<style>
  div {
    width: 400px;
    height: 400px;
  }

  .element1 {
    margin-bottom: 20px;
    background-color: #000;
  }

  .element2 {
    float: left;
    margin-top: 50px;
    background-color: red;
  }
</style>

<body>
  <div class="element1"></div>
  <div class="element2"></div>
</body>
复制代码

从效果上面来看,element1element2的外边距确实已经不再折叠了

  1. 给两个元素的任意一个添加上为display: inline-block属性

跟上一种方法的原理类似,我们给其中一个元素添加上了display: inline-block属性之后,它就已经不算是常规流块盒了,自然也不会用原来的方式布局了,外边距合并自然也就不存在了

<style>
  div {
    width: 400px;
    height: 400px;
  }

  .element1 {
    display: inline-block;
    margin-bottom: 20px;
    background-color: #000;
  }

  .element2 {
    /* display: inline-block; */
    margin-top: 50px;
    background-color: red;
  }
</style>

<body>
  <div class="element1"></div>
  <div class="element2"></div>
</body>
复制代码

直接相邻的父子元素

出现这种问题还是因为常规流块盒的特性:若外边距无缝相邻(没有borderpadding阻隔),则进行外边距合并,跟上面情况的产生原因一样,下面看个示例:

<style>
  .father {
    width: 600px;
    height: 600px;
    margin-top: 10px;
    background-color: red;
  }

  .son {
    width: 300px;
    height: 300px;
    margin-top: 50px;
    background-color: #000;
  }
</style>

<body>
  <div class="father">
    <div class="son"></div>
  </div>
</body>
复制代码

我们希望看到的效果是子元素距离父元素顶部50px,父元素再距离它的父元素顶部10px。但真正的渲染效果是:子元素和父元素的顶部在同一水平线上,并距离顶部50px,并不是我们想要的效果

那么有哪些方法可以实现我们想要的效果呢?

  1. 为父元素添加border属性

我们仔细研究常规流块盒发生外边距合并的特点后会发现,这种现象本质上是因为相邻的块盒外边距无缝接触导致的,也就是说我们只要在这两个外边距中间隔点东西,比如说border边框,就不会发生外边距合并的情况了

<style>
  .father {
    width: 600px;
    height: 600px;
    margin-top: 10px;
    border-top: 1px solid transparent;
    background-color: red;
  }

  .son {
    width: 300px;
    height: 300px;
    margin-top: 50px;
    background-color: #000;
  }
</style>

<body>
  <div class="father">
    <div class="son"></div>
  </div>
</body>
复制代码

我们可以很明显的看到子元素距离父元素顶部50px,父元素也距其顶部10px,恰好就是我们想要的效果;但是这个实现方案有些许不足,因为border毕竟也是盒模型的一部分,在一定程度上占用了盒模型的空间,所以如果强调布局的精确不建议使用此方法

  1. 使用padding替换掉父元素的margin

这种方法的原理和第一种是一样的,本质上就是要阻绝相连的两个常规流块元素边距的无缝连接,只不过第一种方法用的是border阻绝,这里使用的是padding阻绝而已

<style>
  .father {
    width: 600px;
    height: 600px;
    padding-top: 10px;
    background-color: red;
  }

  .son {
    width: 300px;
    height: 300px;
    margin-top: 50px;
    background-color: #000;
  }
</style>

<body>
  <div class="father">
    <div class="son"></div>
  </div>
</body>
复制代码

从效果图来看确实也消除了外边距合并的问题,但是由于padding是在背景颜色background-color的染色范围内,所以如果布局中刚好有设置背景颜色的需求,建议选取其它方法

  1. 让父元素创建BFC

创建了BFC的元素不会与子元素进行外边距合并,这个特性刚好可以帮助我们解决这个问题;同时,个人认为这种方法也是解决外边距合并的最优解

<style>
  .father {
    width: 600px;
    height: 600px;
    margin-top: 10px;
    background-color: red;
    overflow: hidden;
  }

  .son {
    width: 300px;
    height: 300px;
    margin-top: 50px;
    background-color: #000;
  }
</style>

<body>
  <div class="father">
    <div class="son"></div>
  </div>
</body>
复制代码

效果和我们预想的一模一样:

  1. 给父子元素之间添加内联或者匿名元素,添加一个  ,去除折叠了,但是新增的元素会占据额外的空间,所以不推荐
<style>
  .father {
    width: 600px;
    height: 600px;
    background-color: red;
  }

  .son {
    width: 300px;
    height: 300px;
    margin-top: 30px;
    background-color: #000;
  }
</style>

<body>
  <div class="father">
    &nbsp;
    <div class="son"></div>
  </div>
</body>
复制代码
  1. 父子元素之间添加触发了 BFC 的元素,可以添加伪类元素,也可以直接在HTML中添加一个子元素
<style>
  .father {
    width: 600px;
    height: 600px;
    background-color: red;
  }

  .father::before {
    content: '';
    display: flex;
  }

  .son {
    width: 300px;
    height: 300px;
    margin-top: 30px;
    background-color: #000;
  }
</style>

<body>
  <div class="father">
    <!-- <div style="display: flex;"></div> -->
    <div class="son"></div>
  </div>
</body>
复制代码

空的块级元素

当一个块元素上边界margin-top 直接贴到元素下边界margin-bottom时也会发生边界折叠。这种情况会发生在一个块元素完全没有设定边框border、内边距padding、高度height、最小高度min-height 、最大高度max-height 、内容设定为inline或是加上clear-fix的时候

也就是说一个没有设置过宽高、边框、padding的块级元素,如果设置了margin-topmargin-bottom属性的话,那么自身就会发生折叠

<style>
  .element1,
  .element2 {
    height: 150px;
    background-color: aqua;
  }

  .blank {
    margin-top: 200px;
    margin-bottom: 250px;
  }
</style>

<body>
  <div class="element1"></div>
  <div class="blank"></div>
  <div class="element1"></div>
  我是内容
</body>
复制代码

原本位于第二行的空元素设置了距离顶部元素200px,距离底部元素250px,如果不发生折叠,element1和第element2的距离应该是 200+ 0 + 250=450px,然而实际上确只有250px。也就是说这个空元素自身的margin 发生了折叠,只取了大的值250px,另外的200px被折叠且溢出到了空盒子的下面,但有意思的是,溢出的部分就跟失效了一样,并不会影响到element2和下方字体的布局

虽然这种情况遇到的比较少,但也是我们需要注意的点,尽量不要给空盒子同时设置margin-topmargin-bottom属性

不会发生外边距折叠的情况

  • 行内块元素 inline-block 不会发生外边距折叠,包括同层级和嵌套元素
  • 浮动 float 元素不会发生外边距折叠,包括同层级和嵌套元素
  • 绝对定位元素 absolute 还有 fixed 不会发生外边距折叠,包括同层级和嵌套元素
  • 创建了 BFC 的元素和它的子元素不会发生外边距折叠

外边距折叠的计算

同正或同负:选绝对值最大的

同正的情况我们经常会遇到,下面我们来测试一下外边距同负的情况:父元素margin-top值为-10px,子元素margin-top值为-50px,那么最终他们肯定会发生外边距折叠,那么折叠的距离是多少呢?

<style>
  .father {
    width: 600px;
    height: 600px;
    margin-top: -10px;
    background-color: red;
  }

  .son {
    width: 300px;
    height: 300px;
    margin-top: -50px;
    background-color: #000;
  }

  .blank {
    height: 100px;
    background-color: yellow;
  }
</style>

<body>
  <div class="blank"></div>
  <div class="father">
    <div class="son"></div>
  </div>
</body>
复制代码

答案是-50px,选取的是绝对值大的那一个而不是数字大的那一个,所以最终的效果是子元素与父元素顶部在同一水平线上,两者向上合并50px

特殊情况:如果一个外边距是0,另一个外边距是正数怎么计算?

这种情况下,我们将0看成是一个正数,最终的合并距离肯定是两者中最大的那个,也就是另一个元素的外边距的值

有正有负:最大的正边距与最小的负边距的和

简单来说,如果一个元素的外边距是正的,另一个是负的,那么整体合并的边距值就是两者相加的值

<style>
  .father {
    width: 600px;
    height: 600px;
    margin-top: 20px;
    background-color: red;
  }

  .son {
    width: 300px;
    height: 300px;
    margin-top: -50px;
    background-color: #000;
  }

  .blank {
    height: 60px;
    background-color: yellow;
  }
</style>

<body>
  <div class="blank"></div>
  <div class="father">
    <div class="son"></div>
  </div>
</body>
复制代码

父元素的margin-top20px,子元素的margin-top为-50px,最终折叠的距离就是20-50=-30px

特殊情况:如果一个外边距是0,另一个外边距是负数怎么计算?

这种情况下,我们依然将0看成是一个正数,最终的折叠距离还是两者相加,也就是为负数的外边距值

参考文章

本篇文章是自己再阅读了以下文章之后,结合自己的理解作出的一篇总结,希望能够帮助更多的人理解外边距折叠问题

juejin.cn/post/709369…

juejin.cn/post/684490…

猜你喜欢

转载自juejin.im/post/7100373535272468493