简介
相邻的常规流块盒的上外边距(margin-top)和下外边距(margin-bottom)有时合并(折叠)为单个边距,这种行为称为外边距折叠
这段文字看起来可能有些抽象,话不多说,我们先举个例子:
现在有两个垂直方向排列的相邻块级元素,element1
的margin-bottom
属性值为20px
,element2
的margin-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作为间隔距离,这种情况就称之为外边距折叠
外边距折叠触发的条件
同一层级的两个相邻元素
常规流块盒若外边距无缝相邻(没有border
或padding
阻隔),则会进行外边距合并,我们刚刚举的例子就是这种情况,那么这种情况下要怎样才能避免外边距折叠所带来的影响呢?
- 为其中一个元素外层套上一个父元素,并为这个父元素创建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>
复制代码
- 让处在下方的元素浮动
外边距折叠是发生在常规流块盒中的布局方式,而浮动元素已经脱离了文档流,自然也就不会和上面的常规流块盒发生外边距折叠了;但不要给处在上方的元素添加,否则会出现浮动元素与常规流块盒的重叠问题
<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>
复制代码
从效果上面来看,element1
和element2
的外边距确实已经不再折叠了
- 给两个元素的任意一个添加上为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>
复制代码
直接相邻的父子元素
出现这种问题还是因为常规流块盒的特性:若外边距无缝相邻(没有border
或padding
阻隔),则进行外边距合并,跟上面情况的产生原因一样,下面看个示例:
<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,并不是我们想要的效果
那么有哪些方法可以实现我们想要的效果呢?
- 为父元素添加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
毕竟也是盒模型的一部分,在一定程度上占用了盒模型的空间,所以如果强调布局的精确不建议使用此方法
- 使用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
的染色范围内,所以如果布局中刚好有设置背景颜色的需求,建议选取其它方法
- 让父元素创建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>
复制代码
效果和我们预想的一模一样:
- 给父子元素之间添加内联或者匿名元素,添加一个 ,去除折叠了,但是新增的元素会占据额外的空间,所以不推荐
<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">
<div class="son"></div>
</div>
</body>
复制代码
- 父子元素之间添加触发了 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-top
和margin-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-top
和margin-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-top
为20px
,子元素的margin-top为-50px
,最终折叠的距离就是20-50=-30px
特殊情况:如果一个外边距是0,另一个外边距是负数怎么计算?
这种情况下,我们依然将0
看成是一个正数,最终的折叠距离还是两者相加,也就是为负数的外边距值
参考文章
本篇文章是自己再阅读了以下文章之后,结合自己的理解作出的一篇总结,希望能够帮助更多的人理解外边距折叠问题