先记住这个答案
默认 content-box 下,设置的 width 是内容宽度,实际渲染宽度需加上 padding 和 border;而 border-box 的 width 已包含这两者,内容区自动收缩。当容器使用百分比宽度并需要内边距或边框时,border-box 能保证总宽度等于设定值,避免因加法导致换行或溢出。因此栅格布局和通用重置常全局启用 border-box。
- content-box 的 width 不含 padding 和 border
- border-box 的 width 包含 padding 和 border
- 百分比宽度布局应优先用 border-box
两种盒模型的宽度计算机制
在 content-box 下,CSS 的 width 属性指定的是内容区宽度。假设元素 width: 200px,padding-left/right 各 20px,border 左右各 5px,则实际渲染的总宽度是 200 + 202 + 52 = 250px。内容区保持 200px,padding 和 border 向外扩展,导致元素实际占用的空间大于设定值。
border-box 则把 width 定义为包含 padding 和 border 的总宽度。同样 width: 200px,padding 和 border 合计 50px,内容区实际只有 150px。也就是说,设定宽度固定时,padding 和 border 会向内挤压内容区,内容区最小为 0,不能为负。
百分比宽度分栏场景
一个典型的栅格系统,父容器宽度 1200px,需要三栏布局,每栏宽度设为 33.333%,并设置左右 padding 20px 和 border 1px。若使用 content-box,每栏总宽度为 33.333% * 1200 + 40 + 2 = 400 + 42 = 442px,三栏总宽超过 1200px,导致换行。改用 border-box,每栏总宽为 400px,三栏正好排列。
处理方式是将盒模型切换为 border-box,然后在每栏的样式里正常写 width 和 padding。这样百分比宽度始终等于父容器宽度的比例,内边距和边框被包含在内,无需手动用 calc 调整。这也是现代 CSS 重置中常用 * { box-sizing: border-box; } 的原因。
适用边界与副作用
border-box 并非适用于所有场景。当使用 position: relative 或 absolute 时,若需要用 top/left 等属性微调元素位置,content-box 能让偏移量相对于内容区计算,不受 padding 和 border 变化影响,这可能更适合某些精细定位。
另外,如果元素设置了 min-width 或 max-width,这些约束在 border-box 下同样作用于包含 padding/border 的宽度。但若依赖内容区宽度撑开自适应,content-box 的行为更直观。切换为 border-box 后,padding 会挤占内容区,可能破坏基于内容尺寸的自适应布局,此时需权衡或局部重置。
容易答错的地方
- 认为 border-box 包含 margin
- 实际上 margin 从不被计入 width,border-box 只包含 padding 和 border。若需要外边距也参与计算,必须使用 margin 本身或使用数学计算,不能依靠 box-sizing。
- 所有元素都适用 border-box
- 对没有显式 width 的块级元素,width 默认为 auto,此时 border-box 不改变其填充父容器的行为。只有设置了明确宽度或百分比宽度时,差异才有实际影响。
面试官还会怎么问?
为什么浏览器默认表单元素使用 border-box?
因为原生控件如 button、select 等,padding 和 border 样式差异大,使用 border-box 更容易固定按钮尺寸,避免不同样式导致宽度跳动。这也是浏览器默认样式表的行为。
全局设置 border-box 会影响 width: auto 的元素吗?
不会。width: auto 时元素自动填满父容器剩余空间,padding 和 border 减去后内容区随之调整,实际渲染宽度仍为父容器宽度,与 box-sizing 无关。
如何在指定场景临时切换回 content-box?
给单个元素设置 box-sizing: content-box 即可覆盖全局设置。但需注意继承问题,可对需要精确内容宽度的组件局部声明,避免使用通配符造成不必要的副作用。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。