L
L
i
i
r
r
o
o
u
u
s
s
c
c
o
o
d
d
i
i
n
n
g
g
介绍一下css的盒子模型核心知识

何为盒子

在前端开发中,任何眼见之物均可理解为盒子
比方说现在看见的这篇文章,容纳它的就是一个大盒子,然后里面有相应的元素和样式
再比方说我们日常使用的微信和QQ,一个个对话框都是长方形盒子
基于此类,遍地都是

盒子模型的基本要素

盒子的组成部分是由内容(content),内边距(padding),border(边框),外边距(margin)

盒子模型要素

为了方便理解起见,我们从外向里开始介绍

  1. margin :就是指盒子之间的边距
  2. border :就是指盒子的边框
  3. padding :就是指盒子内容与边框之间的间距
  4. content :即剩下部分
    ::: details 套娃的盒子
    我们仔细观察的话就会发现上图就是一个套娃的盒子,从外向内仍然是一个个盒子,只是它们在一起就形成了一个具有完整属性的盒子了
    :::

盒模型

我们不难发现一个问题,就是一个盒子有着 content , padding , border 这些属性,那我们要怎么准确表示它的大小呢?
基于此类问题,我们衍生出以下两种盒模型
::: tip
box-sizing: content-box; /*是W3C盒子模型 */
box-sizing: border-box; /是IE盒子模型/
:::

content-box

标准盒模型(W3C的标准盒模型),其 物理真实宽高 是指由盒子的 border , paddind, content 三部分共同组成的模型
其设置的宽高仅仅指的是 content 的值

例子如下

<style> .content-box { border: dotted blueviolet 2px; padding: 5px; box-sizing: content-box; width: 100px; height: 100px; } </style>
html 复制代码
<div class="content-box"></div>

<style>
  .content-box {
    border: dotted blueviolet 2px;
    padding: 5px;
    box-sizing: content-box;
    width: 100px;
    height: 100px;
  }
</style>
标准盒模型

border-box

怪异盒模型(IE盒模型),其 设置的宽高 为盒子的 border , paddind, content 三部分之和

<style> .box-border { border: dotted blueviolet 2px; padding: 5px; box-sizing: content-box; width: 100px; height: 100px; } </style>
html 复制代码
<div class="box-border"></div>

<style>
  .box-border {
    border: dotted blueviolet 5px;
    padding: 25px;
    box-sizing: border-box;
    width: 100px;
    height: 100px;
  }
</style>
怪异盒模型

区块盒子(块级盒子)与行内盒子

首先我们要了解一个属性 dispaly
该属性定义了元素的显示方式,其值有 blockinlineinline-blockflexgrid

1.行内元素:即 dispalyinline 的元素,它具有以下特点

  • 盒子不会产生换行
  • width 和 height 属性将不起作用
  • 垂直方向的内边距、外边距以及边框会被应用但是不会把其他处于 inline 状态的盒子推开
  • 水平方向的内边距、外边距以及边框会被应用且会把其他处于 inline 状态的盒子推开

2.区块盒子(也称块级盒子):

  • 盒子会产生换行
  • width 和 height 属性可以发挥作用
  • 内边距、外边距和边框会将其他元素从当前盒子周围“推开”
  • 如果未指定 width,方框将沿行向扩展,以填充其容器中的可用空间。在大多数情况下,盒子会变得与其容器一样宽,占据可用空间的 100%

行内盒子

元素1 元素2 元素3
另外一个盒子
<style> .box-inline{ display:inline; background-color:pink; height:500px; width:300px; margin-bottom:100px; } </style>
html 复制代码
<div style="background-color: pink">
  <div>元素1</div>
  <div>元素2</div>
  <div>元素3</div>
</div>
<div>
  另外一个盒子
</div>

<style>
  .box-inline{
      display:inline;
      background-color:pink;
      height:500px;
      width:300px;
      margin-bottom:100px;
  }
</style>

从样式中我们可以看见,元素设置的宽高以及外边距都没有生效,符合上述所说

区块盒子

元素1
元素2
元素3

<style> .box-block{ display:block; background-color:pink; margin-top: 50px; height: 50px; } </style>
html 复制代码
<span class="box-block"> 元素1 </span>
<span class="box-block"> 元素2 </span>
<span class="box-block"> 元素3 </span>

<style>
  .box-block{
      display:block;
      background-color:pink;
      margin-top: 50px;
      height: 50px;
  }
</style>

可以看见,这里的span元素的display被设置为 block ,因此他便具有了上述区块盒子的特性,默认占有百分百宽度,我们设置的高度还有边距也生效了

细心的同学可能已经发现我们特意将 div 设置为 inline,将 span 设置为 block
这么做的目的就是为了更清楚知道 dispaly 对元素的影响,哪怕 spandiv 分别具有默认的 display 值,我们也可以自行覆盖其默认值,同时也理解 spandiv 不过是具有不同显示方式的盒子容器罢了

行内块盒子

基于行内元素无法设置宽高等问题,我们有了inline-block来解决这一问题
其具有以下特点

  • 设置 width 和height 属性会生效。
  • padding、margin 和 border 会推开其他元素。

即有着不占满当前行(不换行)属性的区块盒子

元素1
元素2
元素3

<style> .box-inline-block { display: inline-block; background-color: pink; margin-top: 10px; height: 50px; width: 100px; } </style>
html 复制代码
<span class="box-inline-block"> 元素1 </span>
<span class="box-inline-block"> 元素2 </span>
<span class="box-inline-block"> 元素3 </span>

<style>
  .box-inline-block {
    display: inline-block;
    background-color: pink;
    margin-top: 10px;
    height: 50px;
    width: 100px;
  }
</style>

我们可以很明显看出此时的 span 元素具有了宽高属性,以及margin也可以生效了

外边距折叠问题

区块的上下外边距有时会合并(折叠)为单个边距,其大小为两个边距中的最大值,这种行为称为外边距折叠
::: details 值得知道的是
块的上外边距(margin-top)和下外边距(margin-bottom)有时合并(折叠)为单个边距,其大小为单个边距的最大值(或如果它们相等,则仅为其中一个),这种行为称为边距折叠

这一现象是css的有意为之,能有效避免两者的外边距叠加问题
:::

::: tip 发生外边距折叠的先决条件

  1. 必须是常规文档流中的块级盒子(不是float和absolute定位),并且处于同一个BFC当中
  2. 没有行盒,没有padding和border将他们隔开;
  3. 外边距为垂直方向
    :::

有三种情况会形成外边距折叠:

  1. 相邻的兄弟元素
    相邻的同级元素之间的外边距会被折叠 (除非后面的元素需要清除之前的浮动)
  2. 没有内容将父元素和后代元素分开
    如果没有设定边框、内边距、行级内容,也没有创建区块格式化上下文或间隙来分隔块级元素的上边距与其内一个或多个子代块级元素的上边距;或者没有设定边框、内边距、行级内容、高度或最小高度来分隔块级元素的下边距与其内部的一个或多个后代后代块元素的下边距,则会出现这些外边距的折叠,重叠部分最终会溢出到父代元素的外面
    ::: warning
    tips:简而言之就是子父元素贴在了一起并且没有创建BFC
    :::
  3. 空的区块
    如果块级元素没有设定边框、内边距、行级内容、高度、最小高度来分隔块级元素的上边距及其下边距,则会出现其上下外边距的折叠

外边距折叠例子

相邻的兄弟元素

item 1
item 2
<style> .box-fold :first-child { margin-bottom: 10px; background-color: pink; } .box-fold :last-child { margin-top: 10px; background-color: pink; } </style>
html 复制代码
<div class="box-fold">
  <div>item 1</div>
  <div>item 2</div>
</div>

<style>
  .box-fold :first-child {
    margin-bottom: 10px;
    background-color: pink;
  }
  .box-fold :last-child {
    margin-top: 10px;
    background-color: pink;
  }
</style>

我们为 box-fold 这个盒子下的两个同级子元素分别设置了具有向下的外边距和向上的外边距,但是却发生了外边距折叠问题,这一现象的出现对应上文提到的 原因1
这时如果我们打开F12检查元素的话就会发现这两个div分别具有向下的外边距和向上的外边距,但是总边距并不是20px,而是10px

解决第1类问题

为后面的元素开启浮动

item 1
item 2

<style> .box-fold-1 :first-child { margin-bottom: 10px; background-color: pink; } .box-fold-1 :last-child { margin-top: 10px; background-color: pink; float: left; } </style>
html 复制代码
<div class="box-fold-1">
  <div>item 1</div>
  <div>item 2</div>
</div>

<style>
  .box-fold-1 :first-child {
    margin-bottom: 10px;
    background-color: pink;
  }
  .box-fold-1 :last-child {
    margin-top: 10px;
    background-color: pink;
    float: left;
  }
</style>

此时同级元素之间的外间距折叠问题便得以解决了

没有内容将父元素和后代元素分开

注意下面的盒子和当前段落产生了明显的margin

item 2
<style> .box-fold2 { height: 100px; background-color: red; } .box-fold2 :first-child { margin-top: 50px; background-color: pink; } </style>
html 复制代码
<div class="box-fold2">
  <div>item 2</div>
</div>

<style>
  .box-fold2 {
    height: 100px;
    background-color: red;
  }
  .box-fold2 :first-child {
    margin-top: 50px;
    background-color: pink;
  }
</style>

我们为 box-fold2 这个盒子下的子代设置了向上的外边距,但是结果出乎我们意料,并不是子元素和父元素之间产生了间距,而是父元素产生了间距,这对应的便是上文提到的 原因2
解决方法很简单,为父元素设置边框或者内间距即可,当然也可以开始BFC来解决这一问题

解决第2类问题

item 2
<style> .box-fold2-1 { height: 100px; background-color: red; border: solid black 2px; padding: 1px; } .box-fold2-1 :first-child { margin-top: 50px; background-color: pink; } </style>
html 复制代码
<div class="box-fold2-1">
  <div>item 2</div>
</div>

<style>
  .box-fold2-1 {
    height: 100px;
    background-color: red;
    border: solid black 2px;
    /* padding: 1px; */
  }
  .box-fold2-1 :first-child {
    margin-top: 50px;
    background-color: pink;
  }
</style>

解决原理

对于问题1,我们为第二个子元素设置了浮动,也就意味着第二个脱离了标准文档流(其实也就是创建了 区块格式化上下文)

对于问题2,就是让其不满足外边距折叠的先决条件
(没有行盒,没有padding和border将他们隔开;)

盒子塌陷问题

盒子塌陷是指本应该在父盒子内部的子元素跑到了盒子外部

当父元素没有设置具体大小的时候,子元素设置了浮动属性,子元素就会脱离文档流,相当于是跳出父元素边界
而当父元素的高度为auto时(即没有固定高度),而父元素中又没有其它非浮动的元素时(即无子元素将父元素撑开),父盒子的高度就会直接塌陷为零

这是一个浮动的盒子

这是一个未浮动的盒子
<style> .collapse-box { background: pink; } .float-box { float: left; } .no-collapse-box { background-color: yellowgreen; } </style>
html 复制代码
<div class="collapse-box">
  <div class="float-box">这是一个浮动的盒子</div>
</div>

<br />

<div class="no-collapse-box">
  <div>这是一个未浮动的盒子</div>
</div>

<style>
  .collapse-box {
    background: pink;
  }
  .float-box {
    float: left;
  }
  .no-collapse-box {
    background-color: yellowgreen;
  }
</style>

由于子元素设置了浮动,导致其脱离标准文档流,父元素无法被撑开,从而导致高度塌陷,进而无背景颜色

参考资料
盒子模型
掌握外边距折叠
外边距折叠
区块格式化上下文
盒子塌陷