CSS盒模型
本文最后更新于 2025年8月2日 下午
盒模型
盒模型指的是网页元素的结构,每个元素都是由一个盒子构成。
一个盒子(一个元素)由四部分构成:
- 内容区域(Content)
- 内边距(Padding)
- 边框(Border)
- 外边距(Margin)
所以,元素实际所占宽度等于元素的内容区域、内边距、边框、外边距宽度之和。(高度同理)
盒子类型
box-sizing: content-box
(标准盒模型)
元素的宽度(width)等于元素的内容区域(Content)的宽度。默认值。box-sizing: border-box
(IE 盒模型)
元素的宽度(width)等于元素的内容区域(Content)、内边距(Padding)、边框(Border)宽度之和。
不管是标准盒模型还是 IE 盒模型,元素的宽度(width)都不会包括外边距(Margin)。
元素宽度
将两列内容宽度设置为 70% 和 30%,但它们总共占据宽度超过了可用空间的 100%,这是因为盒模型的默认行为。当给一个元素设置宽或高的时候,指定的是内容的宽或高,元素的所有内边距、边框都是额外追加到宽度上的。
最笨的方法是减少其中一列(比如侧边栏)的宽度。在编程中不推荐魔术数值,因为往往这难以解释一个魔术数值生效原因。如果不理解这个数值是怎么来的,那就不会知道在不同的情况下会产生什么样的结果。替代魔术数值的一个方法是让浏览器帮忙计算,使用 calc() 函数减去内边距、边框、外边距等值,得到刚好 100% 的总和,但还有更好解决办法:修改盒类型。
用通用选择器选中页面上所有元素,并用两个伪元素选择器选中网页的所有伪元素。但是如果在网页中使用了带样式的第三方组件,就可能会因此破坏其中一些组件的布局,尤其是当第三方组件在开发 CSS 的过程中没有考虑到使用者会修改盒模型时。因为全局设置 border-box 时,使用的通用选择器会选中第三方组件内的每个元素,修改盒模型可能会有问题,所以最终需要写另外的样式将组件内的元素恢复为 content-box。
有一种简单点的方式,是利用继承改一下修改盒模型的方式。盒模型通常不会被继承,但使用 inherit 关键字可以强制继承。
元素高度
处理元素高度的方式跟处理宽度不一样。之前对 border-box 的修改适用于高度而且很有用,但通常最好避免给元素指定明确的高度。普通文档流是为限定的宽度和无限的高度设计的。内容会填满视口的宽度,然后在必要的时候折行。因此,容器的高度由内容天然地决定,而不是容器自己决定。
普通文档流:指的是网页元素的默认布局行为。行内元素跟随文字的方向从左到右排列,当到达容器边缘时会换行。块级元素会占据完整的一行,前后都有换行。
控制溢出
当明确设置一个元素的高度时,内容可能会溢出盒容器。当内容在限定区域放不下,渲染到父元素外时就会发生这种现象。用 overflow 属性可以控制溢出内容的行为,支持 4 个值:
- visible(默认值)——所有内容可见,即使溢出容器边缘。
- hidden——溢出容器内边距边缘的内容被裁剪,无法看见。
- scroll——容器出现滚动条,用户可以通过滚动查看剩余内容。在一些操作系统上,会
出现水平和垂直两种滚动条,即使所有内容都可见(不溢出)。不过,在这种情况下,滚
动条不可滚动(置灰)。 - auto——只有内容溢出时容器才会出现滚动条。
请谨慎地使用滚动条。浏览器给网页最外层加上了滚动条,如果网页内部再嵌套滚动区域,用户会很反感。如果用户使用鼠标滚轮滚动网页,当鼠标到达一个较小的滚动区域,滚轮就会停止滚动网页,转而滚动较小的区域。
可以用 overflow-x 属性单独控制水平方向的溢出,用 overflow-y 属性单独控制垂直方向的溢出。但同时给 overflow-x 和 overflow-x 指定不同的值,往往会产生难以预料的结果。
百分比外的方案
用百分比指定高度存在问题。百分比参考的是父元素容器块的大小,但容器的高度通常是由子元素的高度决定的。这会造成死循环,浏览器处理不了,因此它会忽略这个声明。若想让百分比高度生效,就必须给父元素明确定义一个高度。
人们使用百分比高度是想让一个容器填满屏幕。不过更好的方式是用视口的相对单位 vh,100vh 等于视口的高度。还有一个更常见的用法是创造等高列,这不用百分比也能实现。
等高列的问题从 CSS 出现就一直困扰着人们。当时表格是实现等高列的唯一方式。具体来说,是不明确指定高度就能实现等高列的唯一方式,虽然可以简单地将所有列设置高度 500px 或者其他任意值,但是如果要让列自己决定高度,每个元素可能算出来都不一样高,具体的高度取决于内容。
为了解决这个问题,诞生了很多有创意的解决方案。随着 CSS 的演进,出现了伪元素、负外边距等方案。若你还在用这些复杂的方案,那么是时候改变了。现代浏览器支持了 CSS 表格,可以轻松实现等高列,比如 IE8+支持 display: table,IE10+支持弹性盒子或者 Flexbox,都默认支持等高列。
最好办法是让它们自己决定高度,然后扩展较矮的列,让它的高度等于较高的列。CSS 表格和 Flexbox 布局等方式都能实现这种效果。
除非别无选择,否则不要明确设置元素的高度。先寻找一个替代方案。设置高度一定会导致更复杂的情况。
最大最小
可以用 min-height 和 max-height 这两个属性指定最小或最大值,而不是明确定义高度,这样元素就可以在这些界限内自动决定高度。
用 min-height 指定一个最小高度,而不指定它的明确高度。这意味着元素至少等于你指定的高度,如果内容太多,浏览器就会允许元素自己扩展高度,以免内容溢出。同理,max-height 允许元素自然地增高到一个特定界限。如果到达这个界限,元素就不再增高,内容会溢出。
还有类似的属性是 min-width 和 max-width,用于限制元素的宽度。
外边距
负外边距
不同于内边距和边框,外边距可以设置为负值。负外边距有一些特殊用途,如让元素重叠或者拉伸到比容器还宽。
负外边距的具体行为取决于设置在元素的哪边。若设置左边或者顶部的负外边距,那么元素就会相应地向左或向上移动,导致元素与它前面的元素重叠;若设置右边或者底部的负外边距,那么并不会移动元素,而是会将它后面的元素拉过来。给元素底部加上负外边距并不等同于给它下面的元素顶部加上负外边距。
如果不给一个块级元素指定宽度,那么它会自然地填充容器宽度。但是如果在右边加上负外边距,则会把它拉出容器,如果又在左边再加上相等的负外边距,元素的两边都会扩展到容器外面。
如果元素被别的元素遮挡,利用负外边距让元素重叠的做法可能导致元素不可点击。负外边距并不常用,但是在某些场景下很实用尤其是当创建列布局的时候。不过应当避免频繁使用,不然网页的样式就会失控。
外边距折叠
在 CSS 中两个或者多个普通流中相邻盒子的外边距在垂直方向上发生重叠,只产生单个外边距的这种现象叫外边距折叠。这里相邻盒子可能是相邻也可能是嵌套,外边距折叠分为父子外边距折叠和兄弟外边距折叠。
分析
外边距折叠的折叠值等于相邻外边距中的最大值。
即使两个元素不是相邻的兄弟节点,即使将两个元素都额外用一个额外的 div 标签包裹起来,也还会有外边距折叠。在没有其他 CSS 的影响下,所有相邻元素的顶部和底部外边距都会发送外边距折叠。
总之,所有相邻的顶部外边距和底部外边距会折叠到一起。如果只是在页面中添加了一个空的、无样式的 div(没有高度、边框和内边距),它自己的顶部和底部外边距也会折叠。
可以给任何元素加上外边距,而不必担心它们前后的元素是什么。
触发
以下条件都满足会触发:
- 都是普通流中的元素且属于同一个 BFC
- 没有被内边距、边框、非空内容隔开
- 两个或两个以上垂直方向的“相邻元素”,“相邻元素”可能是父子节点也可能是兄弟节点
防止
要避免外边距折叠只需破坏掉触发的条件即可,比如创建一个 BFC。两个元素只有在同一 BFC 内才有可能发生垂直外边距的重叠,包括父子节点、兄弟节点。要解决外边距重叠问题,只要让它们不在同一个 BFC 内就行。
解决:
- 对于兄弟节点,只要让其中任一的一个元素触发 BFC,就能使它们相邻的外边距不重叠
- 对于父子节点,只要让一个元素的父级元素触发 BFC,就能使父元素和子元素的外边距不重叠
容器内元素间距
容器元素的内边距和容器子元素的外边距之间的相互作用处理起来很棘手。
有好几种方法可以解决该问题。使用相邻的兄弟组合器(+)来选中同一个父元素下紧跟在首个元素后面的元素,这样只在两个元素之间存在外边距。但每一次改变 HTML,都需要考虑这些外边距的问题。你得确保每个元素之间有间距,但是容器的顶部(或底部)没有多余的间距。
不要给网页当前的内容固定外边距,而是应采取更通用的方式,不管网页结构如何变化都能够生效。这就是猫头鹰选择器:会选中页面上有着相同父级的非第一个子元素。猫头鹰选择器开头是一个通用选择器,它能选中所有元素,后面是一个相邻兄弟组合器,最后是另一个通用选择器。
也许会担心通用选择器的性能问题。现在不必担心,因为现代浏览器都能很好地处理。此外,猫头鹰选择器可能会减少样式表中的选择器数量,因为它在全局范围内处理了大多数元素的间距问题。实际如果你的样式表要处理的细节很多的话,猫头鹰选择器的性能可能更好。
这样使用猫头鹰选择器是需要权衡的。它省去了许多的需要设置外边距的地方,但是在某些不想加外边距的地方则需要覆盖。通常只在有并列元素,或者有多列布局时这样使用。有时还需要根据设计,给段落和标题设置特定的外边距。