CSS外边距合并&块格式上下文

前言

之前在前端开发的过程中,都没有遇到外边距合并的问题(其实是因为大多时候都直接用前端样式库(〃'▽'〃)),这一次需要动手排一个页面,也挺简单,但是遇到了一个奇怪的问题,所以学习记录一下。

问题

测试代码如下:

<!doctype html>
<html class="no-js" lang="">

<head>
    <meta charset="utf-8">
    <meta http-equiv="x-ua-compatible" content="ie=edge">
    <title>test</title>
    <meta name="description" content="">
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<style>
* {
    margin: 0;
    padding: 0;
}

body {
    max-width: 750px;
    margin: 0 auto;
}

.div1 {
    background: yellow;
    height: 200px;
}

.div2 {
    background: blue;
    height: 200px;
}

.div2Child {
    background: red;
    height: 100px;
}
</style>

<body>
    <div class="div1">我是吃瓜群众</div>
    <div class="div2">
        <div class="div2Child" style="margin-top: 50px">我是子div</div>
        <p style="color: white">我是父div</p>
    </div>
</body>

</html>


子div设置了margin-top之后,父div也跟着一起margin-top了。

原因如下:

Margin Collapsing 外边距合并

块的顶部外边距和底部外边距有时被组合(折叠)为单个外边距,其大小是组合到其中的最大外边距,这种行为称为外边距塌陷(margin collapsing),或外边距合并。

外边距合并的3中基本原因:
1、相邻的元素
毗邻的两个兄弟元素之间的外边距会合并

2、块级父元素与其第一个/最后一个子元素

  • 如果块级父元素中,不存在border, padding, inline part, block formatting context created, or clearance 来隔离第一个子元素的上边距,就会发送外边距合并现象。
  • 如果块级父元素,不存在border, padding, inline content, height, min-height, max-height来隔离下边距,则会和最后一个子元素合并。

3、空块元素
如果存在一个空的块级元素,不存在 border、padding、inline content、height、min-height来隔离上下外边距,那么它的上下外边距将会合并。

  • 当都为正数时,取两者中较大者。
  • 当都为负数时,取绝对值较大者。
  • 当一正一负时,取相加之和。
  • 外边距设为0时,这些规则也仍旧生效。

BFC(Block Formatting Context 块格式化上下文)与元素外边距合并 :

  • 当两个元素属于不同的BFC时,这两个元素的外边距不会合并
  • 但在同一个BFC内,两个相邻元素的外边距仍会合并。

Block Formatting Context 块格式化上下文

一个块格式化上下文(block formatting context) 是Web页面的可视化CSS渲染出的一部分。它是块级盒模型出现的区域,也是浮动元素与其他元素进行交互的区域。

一个块格式化上下文由以下之一创建:

  • 根元素或一些包含它的元素
  • 浮动元素 (元素的 float 不是 none)
  • 绝对定位元素 (元素的 position 为 absolute 或 fixed)
  • 内联块 (元素具有 display: inline-block)
  • 表格单元格 (元素具有 display: table-cell,HTML表格单元格默认属性)
  • 表格标题 (元素具有 display: table-caption, HTML表格标题默认属性)
  • display隐式创建的匿名表格列,包括table, table-row, table-row-group, table-header-group, table-footer-group (这些都是html的默认样式), or inline-table
  • 具有overflow 且值不是 visible 的块元素,
  • display: flow-root
  • flex 项目
  • grid 项目
  • 多列容器(column-count 和 column-width 不是 auto, 包括 column-count: 1的元素)
  • column-span: all 应当总是会创建一个新的格式化上下文,即便具有 column-span: all 的元素并不被包裹在一个多列容器中。

解决方案

知道了问题原因所在,以及了解了相关原理,就很好办了。
1、给div2设置border
为了不改变div2的大小,还需将设置 box-sizing为border-box将padding和border包含在定义的width和height之内

.div2 {
    background: blue;
    height: 200px;
    border-top: 1px solid transparent;
    box-sizing: border-box;
}

2、去掉div2Child的margin,改为设置div2的padding

.div2 {
    background: blue;
    height: 200px;
    padding-top:50px;
    box-sizing: border-box;
}

3、div2设置为内联元素

.div2 {
    background: blue;
    height: 200px;
    display: inline-block;
    width: 100%;
}

4、清除浮动
所有浮动元素包含在div2内,增加样式clearfix

.clearfix::before, .clearfix::after{
    overflow: hidden;
    display: table;
    visibility: hidden;
    content: '';
    clear: both;
}

或者创建一个新的BFC,使其在不同的上下文中,不合并外边距。
5、给div2设置Position

.div2 {
    background: blue;
    height: 200px;
    position: absolute;
    width: 100%;
    max-width: 750px;
}

6、给div2设置float,注意后面的元素需要clear浮动

.div2 {
    background: blue;
    height: 200px;
    float: left;
    width: 100%;
}

7、给div2设置display

.div2 {
    background: blue;
    height: 200px;
    display: inline-block;
    /*display: table;*/
    width: 100%;
}

8、给div2设置flex

.div2 {
    background: blue;
    height: 200px;
    display: flex;
    flex-direction: column;
}

9、给div2设置overflow

.div2 {
    background: blue;
    height: 200px;
    overflow: hidden;
}

万变不离其宗

参考

https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Model/Mastering_margin_collapsing
https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Block_formatting_context

posted @ 2017-12-22 21:24  leestar54  阅读(248)  评论(0编辑  收藏  举报