CSS 盒模型扩展阅读

294 阅读5分钟

盒模型概念

CSS 盒模型由四部分组成:marginborderpaddingcontent

css-box-mode.png

每一种边界所包裹的区域对应一种 box。例如 content edge 所包裹的区域就成为 content-box,另外三种分别是:padding-boxborder-boxmargin-box。这四种 box 在位置大小上依次是包含关系

盒模型的应用

1. 改变盒子的大小

属性 box-sizing 目前支持两种盒模型:content-box(默认)、border-box(max-、min-)width(max-、min-)height 根据 box-sizing 的取值分别对应上图相应 edge 所包裹的地方。

2. 改变背景图像(background-image)绘制区域

属性 background-origin 规定 background-position 相对于什么位置来定位,它的取值有三种:content-boxpadding-boxborder-box。但是背景颜色不受此影响,总是从 boder 开始渲染。

3. 改变背景裁剪区域

属性 background-clip 的取值与 background-rogin 一样,表示背景裁剪区域范围

盒模型外边距

1. 外边距可以是负数

在 CSS 盒模型中,margin 比较复杂,其他三种属性只能大于等于 0 的数值,而 margin 可以是负数。正数表示推开其他盒子,那么负数就是拉拢。

2. 百分比取值

无论是 margin-left 还是 margin-top,其百分比数值是根据父盒子的逻辑宽度计算的,这里宽度不一定是屏幕宽度,与 writing-mode 有关系。如果是水平书写,宽度就是物理上的宽度;如果是垂直书写方式,宽度就是高度。

3. 垂直方向合并

垂直相邻的两个 margin 数值符号相同,合并后的外边距取绝对值较大的数,并带上符号。符号不同,则合并后的外边距是两数相加的结果。

两个垂直相邻外边距发生合并,必须满足以下几个条件:

  • 正常文档流中处于同一个块格式化上下文的块级盒子
  • 中间没有其他东西遮挡,比如:行内盒子、清除浮动、paddingborder
  • 且满足以下任意一个条件:
    1. 父盒子与正常流中第一个子盒子的 top margin
    2. 正常流中相邻的两个块级盒子的 bottom margintop margin
    3. 正常流中最后一个子盒子与父盒子的 bottom margin,且父盒子是自动计算高度
    4. 同一个块盒子只有top marginbottom margin,高度为 0

有两种例外是不会发生外边距合并的:

  • 根元素的外边距
  • 清除浮动的元素,其自身上下外边距发生合并后,不会再与父盒子的 bottom margin 发生合并(外边距是可以多重合并的)

4. 外边距合并示例

4.1 最常见的上下合并

  * {
    margin: 0;
    padding: 0;
  }
  html {
    background-color: rgb(200, 228, 146);
  }

  div {
    height: 100px;
    width: 400px;
  }

  #first {
    background-color: red;
    margin-bottom: 20px;
  }
  #second {
    background-color: blue;
    margin-top: 20px;
  }
<body>
    <div id="first"></div>
    <div id="second"></div>
</body>

上下div外边距合并@2x.png 上下两个 divmaring-bottommargin-top 都是 20px,两个数相加是 40px,而实际渲染出来的只有 20px。

4.2 父与子的 margin-top 合并

#parent {
    margin-top: 10px;
    height: 200px;
}
#first-child {
    background-color: red;
    margin-top: 20px;
    height: 150px;
}
<body>
    <div id="parent">
      <div id="first-child"></div>
    </div>
</body>

父与第一个子合并@2x.png

从上面的截图可以看出,可以发现 .first-child 元素设置的 margin-top 直接使 .parent 与顶部拉开了 20px 的距离(但是没有在父与子元素之间撑开),这个距离是两个 div margin-top 合并后的值。以前经常有开发人员遇到这样的情况,明明没有给上边框设置 margin-top,却产生了一小片空白。

4.3 自身上下外边距合并

自身上下外边距合并@2x.png 图中 div 高度为 0,它上下外边距会发生了合并,两个 p 元素之间只隔了 20px。

4.4 最后一个子元素与父元素 bottom margin 合并情况

第一种:最后一个子元素与父元素 bottom margin 合并

* {
    margin: 0;
    padding: 0;
}
html {
    background-color: rgb(200, 228, 146);
}
.parent {
    margin-bottom: 10px;
    background-color: red;
}
.parent > :last-child {
    margin-bottom: 40px;
}
<div class="parent">
  <p>假装有内容</p>
  <p>假装有内容</p>
  <p>假装有内容</p>
  <p>假装有内容</p>
  <div>我离木头人的距离是40px</div>
</div>
<p>木头人</p>

image.png 将【木头人】往下推了40px

第二种:最后一个子元素自身上下外边距合并,再与父元素 bottom margin 合并

.parent {
    margin-bottom: 10px;
    background-color: red;
}
.parent > :last-child {
    margin-top: 40px;
    margin-bottom: 40px;
}
<div class="parent">
  <p>假装有内容</p>
  <p>假装有内容</p>
  <p>假装有内容</p>
  <p>假装有内容</p>
  <div></div>
</div>
<p>木头人</p>

image.png 同样将【木头人】往下推了40px,但没有在父元素底部内撑开。

第三种: 上下外边距合并(没有 borderpaddingcontent),并且有清除浮动,则合并后外边距不再与父元素 margin-bottom 不合并

* {
    margin: 0;
    padding: 0;
}
html {
    background-color: rgb(200, 228, 146);
}
.parent {
    margin-bottom: 10px;
    background-color: red;
}
.parent > :last-child {
    margin-bottom: 40px;
    clear: left;
}
<body>
    <div class="parent">
      <p>假装有内容</p>
      <p>假装有内容</p>
      <p>假装有内容</p>
      <p style="float:left">下面的div外边距没有和父元素合并</p>
      <div></div>
    </div>
    <p>木头人</p>
</body>

image.png 父元素底部被撑开了 40px,并且父元素还推开了【木头人】10px,说明父元素的下外边距没有合并

总结

CSS 盒模型中垂直方向外边距合并情况较为复杂,发生合并有两个重要条件,一是相同的块格式化上下文,二是元素在正常文档流中。行内盒子垂直方向外边距没有效果,并且widthheight 设置也无效。