![d587cbe56121f481e68b2afb9627ec01.png](https://i-blog.csdnimg.cn/blog_migrate/dfdbc3e9a50f3245c6e99ae0a9376fed.jpeg)
在用 CSS 布局的时候,经常会有各种怪异的情况出现,有些元素就是不在它应该在的位置上,也显示不成该有的大小,页面乱七八糟。这些其实还是因为 CSS 的布局基础没有掌握牢固且不够熟练。今天就深入了解一下 CSS 最基础也是最重要的部分 - 盒子模型,分别会讲到盒子的概念、布局方式、box-sizing 属性、特殊的盒子模型以及边距塌陷,相信你掌握了它们之后就能解决一大半的布局问题。
盒子模型
在 CSS 盒子模型中,每个的 HTML 元素都会被当作是一个矩形的盒子,然后对它们进行从上到下,从左到右的布局。
一个盒子通常包括四个区域,从里到外分别是:
- 内容区域 - 代表盒子的实际内容部分,它是由 width 宽度和 height 高度来确定的
- 内间距区域 - 代表盒子与实际内容之间的空白区域,由 padding 属性设置
- 边框区域 - 代表盒子的边框,是内间距区域和外边距区域的边界,由 border 属性设置
- 外边距区域 - 代表此盒子的边框与相邻的其他盒子边框之间的距离,由 margin 属性设置
![ceafac9b08f6aef8a1f0bb4a4d1dc351.png](https://i-blog.csdnimg.cn/blog_migrate/835efcaeb58b551064f93a17d95e7c52.jpeg)
那么怎样通过这四个区域计算盒子的最终宽度和高度呢?答案是一个盒子的宽度和高度的计算方式,会根据盒子的类型而不同。
盒子的类型
CSS 中的盒子模型有两种类型,一种是 content-box,内容盒子,一种是 border-box 边框盒子,通过 css 属性 box-sizing 来设置。这两种盒子计算宽高的方式不一样。我们先来看一下 content-box 。
content-box
content-box 是 box-sizing 的默认值,也就是说所有的盒子默认都是内容盒子,那么这里 css 属性中的 width 和 height 设置的是它内容区域的宽高,而盒子的宽高需要加上内间距和边框,外边距不算在内。
宽度的计算方式是:内容的宽度+左右内间距+左右边框的宽度
高度的计算方式则是:内容的高度+上下内间距+上下边框的宽度
比如说有一个盒子,宽度设置为 300px,高度设置为 200px,内间距设置为 10px,边框设置为 5px,外边距设置为 20px,那么盒子的宽度则是 300 + 10 + 10 + 5 + 5 = 330,高度则是 200 + 10 + 10 + 5 + 5 = 230:
![d3738e74251dd7494b4cf0a8ede142c3.png](https://i-blog.csdnimg.cn/blog_migrate/98b4abdac6453899b03510203f0b75a4.png)
运行效果(点击这里查看真实的示例,可用谷歌开发者工具):
![0763bea7a7e13790530b5b1d7140997c.png](https://i-blog.csdnimg.cn/blog_migrate/751200e0bc4037384375a9731043ae0e.png)
border-box
如果把 box-sizing 的值改成 border-box,那么 width 和 height 属性就是分别设置盒子的最终宽度和高度
![dae6ee1e77c78d064a8b5cd39a893819.png](https://i-blog.csdnimg.cn/blog_migrate/d1e98d16c08e110f8d440648e03c3791.png)
![d70b6890c532d9667865e49da7f4dd87.png](https://i-blog.csdnimg.cn/blog_migrate/4a00865e1f3dc674827f76aeb8f63395.png)
可以通过浏览器开发者工具看到真实的内容宽度变成了 270,高度变成了 170,加上边框和间距之后,才是设置的 width 和 height 的值
特殊的盒子-替换元素
css 盒子模型中还有一种特殊的盒子:替换元素。替换元素是说,它们加载的是外部内容,并不会受现有的 CSS 属性的影响。常见的替换元素有 <img>
、 <iframe>
、 <video>
等,这些标签都是用来加载外部文件的,当前 HTML 页面中的 CSS 样式并不会影响它们的内容,比如说 <iframe>
标签加载的子页面,不会受当前页面的 CSS 影响。不过,可以通过 CSS 来控制替换元素在盒子中的位置,比如说用 <img/>
加载一张图片,我们可以限制 <img/>
盒子的宽高,然后利用 object-fit 属性,让图片等比例占满整个盒子,裁掉显示不下的部分,然后通过 object-position 来设置图片在盒子中的位置,可以是靠下或者居中等:
![c480bb7079d240768824a753688d907e.png](https://i-blog.csdnimg.cn/blog_migrate/dc1e2946332d1d5d2c6bc2fe4f600600.png)
![9588cb2f4da0bb9cc966305b3c7cc50f.png](https://i-blog.csdnimg.cn/blog_migrate/e159730ada6f4cb9e095667cc1894810.png)
![82a0100ae6dad129aeeb25ab585adcf0.png](https://i-blog.csdnimg.cn/blog_migrate/9520c72036d30866c4c16372cf92ceb8.jpeg)
盒子的布局方式
多个盒子之间会有不同的布局方式,它是通过设置显示类型来实现的。根据盒子本身与外部相邻的其他盒子之间的排列方式,还有盒子内部的子盒子之间的排列方式,我们可以把它们分为外部显示类型和内部显示类型两种。
外部显示类型
外部显示类型控制的是相邻盒子之间的布局,分为块级盒子和行内盒子两种。它们是浏览器默认的布局方式。
块级盒子
块级盒子,比如 div,p,h1 等,它的宽度(指盒子的整体宽度)会占满整个浏览器,并且后边的盒子会被挤占到下一行去显示。
![eaa4605d0aa4b9e56b7f7ea5d796cd3f.png](https://i-blog.csdnimg.cn/blog_migrate/c2500e9f107738da295864713704a35c.png)
行内盒子
行内盒子比如 span, a 等,它的宽度是内容的宽度,后边的盒子会跟在它的后边继续排列。
![1da0cbff04b1fc7c06cb9f1e75b48504.png](https://i-blog.csdnimg.cn/blog_migrate/13959921bcf96b23e56d3d0349f54781.png)
它们两者的区别是:
行内盒子无法手动设置宽高,并且垂直方向上的 padding 和 margin 虽然能够设置生效,但并不会挤占其他盒子的空间,会导致重叠。不过水平方向上的则会挤占其他盒子的空间:
![daffb751e8ba74cbb6563f9a508848f3.png](https://i-blog.csdnimg.cn/blog_migrate/8188574a9c7ae4bc0acedb0380e2128f.png)
块级盒子的宽高、padding 和 margin 则都会生效并且挤占空间。
![4d72b81a4edd00a2b2ab15be5b29f690.png](https://i-blog.csdnimg.cn/blog_migrate/c904693c7017da9aa0323123732cb4df.png)
内部显示类型
内部显示类型则是控制这个盒子内部的子盒子之间的排列方式,上边说的 block 块级和 inline 行内盒子是正常的文档流,块级盒子内部的盒子也是按照这种方式排列,要注意的是行内盒子里边只能有行内盒子,不能有块级盒子。我们可以通过设置 display 的值来修改内部盒子的排列方式。
比如设置为 flex 可以变成流式布局 :
![fbf80ccd4b397d70ae66dfdabdfbcf36.png](https://i-blog.csdnimg.cn/blog_migrate/9c4b0e3709a4b1fc85b18c5cc6f51ef3.png)
设置为 grid 可以变成栅格布局 ,它们属于非正常的文档流。
![6b0ab970fc6db85210c07d39ecebf3bb.png](https://i-blog.csdnimg.cn/blog_migrate/28b2c07e19ce40fedeb9caa1d7f48743.png)
inline-block 显示类型
如果想让一个盒子的外部类型保持 inline 行内状态,不把后边的盒子挤占到下一行,但是又想同时设置宽高、padding 和 margin,那么可以使用 inline-block 这个显示类型,这样这个盒子就有了宽高,并且无论是水平还是垂直方向上的 padding 和 margin,都可以把周围的盒子的空间挤占掉。
![af05676a5146f3f30244439ebd7715a7.png](https://i-blog.csdnimg.cn/blog_migrate/84f798d9bdeb17ce1c5c39a7233346a5.png)
![270fe116162598f509391e585446b5c3.png](https://i-blog.csdnimg.cn/blog_migrate/aa89aadbd323e7fdea1ba9fea4101f1c.png)
边距塌陷
CSS 盒子模型中,有一个问题需要特别注意一下,如果给两个盒子同时设置外边距,那么它们最终的距离很可能并不是两个盒子的外边距之和,这种情况会发生在相邻盒子之间,也会发生在父子盒子之间。当它们同时设置了边距时,如果都是正数,则取最大值,如果相同,则取其中之一,如果有正有负,则取最大的正数加上最小的负数之和,如果都是负数,则取最小值。
相邻盒子之间
相邻的盒子之间的边距一般都会有塌陷的情况,比如一个盒子的下边距和下面盒子的上边距:
![6de700fbf3129679e8c8e7ebe0df7d5d.png](https://i-blog.csdnimg.cn/blog_migrate/93136bbb7c571672798c472c021ad057.png)
这里虽然给.p2 设置了上边距为 10px,但是他们两个的边距并不是 30px,而是取的两者的最大值 20px。
父子盒子之间
父子盒子的边距也会塌陷,而且塌陷时,边距会放置在父盒子的外边,子盒子和父盒子之间的边距并不会生效:比如这里,.box 和 p1 的上边距塌陷了,使用了 box 设置的值 20px,而不是 30px,并且边距在 box 的外侧,p1 和 box 之间没有边距。
![a789183ecd2710833424e559a208541d.png](https://i-blog.csdnimg.cn/blog_migrate/728b4a8cccb34d3a15fb920bbe0d7605.png)
如果两个盒子之间有 border、padding 或者 BFC 的话,就不会有边距塌陷:
![93b36329f563919bd83b3463654556bd.png](https://i-blog.csdnimg.cn/blog_migrate/8d5356590072686a855ea99284b9242c.png)
下边距塌陷也是一样的,也在父盒子的外侧,这里给 box 和第二段文本设置下边距,可以看到最终下边距是 30px:
![111a553b162241f471e9a4421f9fe728.png](https://i-blog.csdnimg.cn/blog_migrate/e37935f34aafad8211d150f8b68bb0a3.png)
![0d683a8197e23625acf599639f3699a6.png](https://i-blog.csdnimg.cn/blog_migrate/4f4cfcd0b3d7a0edc372b2cfa8926d0d.png)
如果下边距塌陷的盒子中间有 border、padding,或者设置了 height、min-height 等属性,就不会有塌陷,这里如果把 box 的高度设置为 150px,那么第二段文本的下边距就不会塌陷了:
![817ef24233f9fa91ed09f5bf120d33a8.png](https://i-blog.csdnimg.cn/blog_migrate/b09bcf8cd1f80832c4265119c0a11f2f.png)
总结
我们总结一下 CSS 盒子模型的概念:
- 它是由内容区域、内间距区域、边框区域和外边距区域构成的
- 根据 box-sizing 属性的不同,盒子的宽高计算方式也不
- 有块级盒子和行内盒子两种
- 可以通过设置外部和内部显示类型改变布局方式
- 还有一种特殊盒子 - 替换元素
- 最后我们了解到了盒子的边距塌陷的情况
CSS 盒子模型是熟练布局 HTML 页面的基础,且是重中之重,只有掌握了它才能够对页面进行精确的布局,准确的预测一个元素应该在哪个位置和它的尺寸,这样就能够避免元素总是不在自己的位置上,且元素占用的空间跟想像的不一样的情况了。
![f7dc4f96599d018276ddd3e6bed85964.png](https://i-blog.csdnimg.cn/blog_migrate/52820f083e5c608e4c192209a92bcda6.jpeg)
如果你是前端小白或者打算转行进入前端这个行业,不知道未来该怎么学习,都可以加入我组建的自学团,每天一起打卡学习,分享学习资料,定期组织实战项目,匹配学习伙伴,我会严格监督你们的。
作者:峰华前端工程师
链接:一文搞懂 CSS 盒子模型:概念、种类、布局方式、边距塌陷全都在这里了