CSS / padding 与 margin:间距设置、宽度计算和外边距折叠

在 CSS 中,padding 是内容与边框之间的空间,而 margin 则在边框之外。使用内边距可以扩展元素内部,包括其背景;使用外边距可以调整它与周围元素的间距。最终宽度取决于 box-sizing,并且符合条件的垂直外边距可以折叠,而非简单相加。

padding 与 margin 的区别

一个元素的盒子由内容、内边距、边框和外边距组成,从内到外排列。它的背景通常会延伸到内边距区域,而外边距是透明的。增加按钮的内边距通常会扩大其背景和可点击区域;增加外边距不会使外部间隙可点击。

特征 padding(内边距) margin(外边距)
位置 内容和边框之间 边框之外
普通背景色 也会在内边距区域绘制 透明
负值 不允许 允许
auto 不允许 允许;根据布局规则解析

这两个属性接受如 px、rem 和 % 的值,但 padding: auto 和负内边距无效。在普通横向书写模式下,这些物理方向属性的百分比都以包含块的宽度为基准,上、下方向也一样,不是按其高度计算。W3C 盒模型和间距定义

读懂一到四个值的方向

四值简写从顶部开始,按顺时针顺序:右、下、左。两个值表示垂直和水平;三个值表示上、水平、下。同样的规则适用于内边距和外边距。

示例 上 右 下 左
padding: 10px 10px 10px 10px 10px
padding: 10px 20px 10px 20px 10px 20px
padding: 10px 20px 30px 10px 20px 30px 20px
padding: 10px 20px 30px 40px 10px 20px 30px 40px

对于一侧,使用类似 padding-top 或 margin-left 的属性。后续的 padding 或 margin 简写可以覆盖之前声明的侧面值,因此请检查规则的顺序。

通过完整示例检查实际宽度

将以下完整代码保存为 UTF-8 格式的 spacing.html 并在浏览器中打开。不需要任何库或服务器。Alpha、Beta 和其他单词是比较标签。编辑后,保存并重新加载。在开发者工具中选择每个 .box 并检查其盒模型图或计算样式,以检查宽度和间距。

<!doctype html>
<html lang="zh">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>padding 与 margin 比较</title>
  <style>
    body { font-family: sans-serif; max-width: 640px; margin: 0 auto; padding: 16px; }
    h2 { font-size: 1.1rem; }
    .stage { padding: 12px; border: 1px dashed #777; overflow-x: auto; }
    .box { width: 200px; padding: 16px; border: 4px solid #17324d; margin: 24px; background: #dcefff; }
    .content-box { box-sizing: content-box; }
    .border-box { box-sizing: border-box; }
    .asymmetric { box-sizing: border-box; padding: 8px 16px 24px 32px; margin: 10px 20px 30px 40px; }
    .center { box-sizing: border-box; margin: 0 auto; }
    .collapse-demo { display: flow-root; border: 1px dashed #777; }
    .item { height: 30px; background: #dcefff; }
    .first { margin: 0 0 24px; }
    .second { margin: 16px 0 0; }
  </style>
</head>
<body>
  <h1>padding 与 margin 比较</h1>
  <h2>content-box</h2>
  <div class="stage"><div class="box content-box">Alpha</div></div>
  <h2>border-box</h2>
  <div class="stage"><div class="box border-box">Beta</div></div>
  <h2>padding / margin</h2>
  <div class="stage"><div class="box asymmetric">Gamma</div></div>
  <h2>margin: 0 auto</h2>
  <div class="stage"><div class="box center">Delta</div></div>
  <h2>margin-bottom / margin-top</h2>
  <div class="collapse-demo">
    <div class="item first">A</div>
    <div class="item second">B</div>
  </div>
</body>
</html>

Alpha 连边框在内的宽度为 200 + 16 × 2 + 4 × 2 = 240px。包括其水平外边距,它占据 240 + 24 × 2 = 288px。Beta 使用 border-box 尺寸计算方式,因此其 border-box 宽度为 200px,其内容宽度为 200 - 16 × 2 - 4 × 2 = 160px。这些计算不包括外层 stage 元素的内边距和边框。

Gamma 在顶部有 8px 的内边距,右侧 16px,底部 24px,左侧 32px。它的外边距依次为 10px、20px、30px 和 40px。与上面的简写方向比较可见差异。

使用 box-sizing 指定尺寸计算方式

使用 content-box 时,width 指定内容宽度,内边距和边框会加在内容之外。使用 border-box 时,指定的宽度包括内容、内边距和边框。无论哪种模式,指定的 width 都不包含 margin。普通的 div 元素初始使用 content-box,但网站的重置 CSS 可能会更改此设置;请检查计算后的值。

将示例中的 .content-box 规则替换为以下内容。Alpha 连边框在内的宽度从 240px 变为 200px。保持其他值不变,进行比较,然后恢复 content-box。如果内边距和边框之和超过指定宽度,内容宽度不会变为负值,因此最终的盒子仍可能比预期更宽。

.content-box {
  box-sizing: border-box;
}

MDN 对 box-sizing 的解释

使用自动外边距水平居中

要在正常流中水平居中一个块,盒子的宽度必须小于其父元素的可用宽度,并且左右边距必须为自动。Delta 的 width 为 200px,并设置了 margin: 0 auto,因此剩余的水平空间会在两侧共享。仅靠居中不能解决当父元素太窄时的溢出问题。

如果一个普通块的自动宽度已经填满了可用水平空间,添加左右自动外边距将不会产生可见的居中效果。在正常流中,margin-top: auto 和 margin-bottom: auto 不会将其垂直居中。Flexbox 或 Grid 中的自动外边距遵循不同的布局规则,因此要区分这些上下文。

理解外边距折叠和负值

在普通的块布局中,相邻的可折叠垂直外边距可以合并。在最后一个例子中,A 的 24px 底部外边距和 B 的 16px 顶部外边距都是正值,因此它们之间的间距取较大值 24px,而不是 40px。父元素的边框在此例中阻止了该父元素与其子元素之间的外边距折叠。

将示例的 .collapse-demo 规则更改如下。其子元素变为弹性子项,它们的外边距不会彼此合并,因此间距变为 24 + 16 = 40px。对比并恢复 display: flow-root。仅对父元素应用 flow-root 并不能消除兄弟块之间的所有外边距折叠。

.collapse-demo {
  display: flex;
  flex-direction: column;
  border: 1px dashed #777;
}

在此处使用的横向书写模式中,左右外边距不会发生这种折叠。当发生折叠的外边距包含正值和负值时,将最大正值加到绝对值最大的负值上:24px 和 -8px 产生 16px。如果全部为负值,则使用最小的负值(绝对值最大);-8px 和 -16px 产生 -16px。像 margin-left: -8px 这样的声明是有效的,但可能导致内容重叠或移出其父元素。检查窄屏和键盘焦点指示器。负内边距不能产生相同的效果。 W3C 外边距折叠规则

间距不符合预期时应检查什么

  1. 检查浏览器默认设置。默认的 body 外边距或者 p 和 h1 的默认垂直外边距可能会使间距看起来比你的声明更大。请重置相关元素,而不是盲目地删除所有间距。
  2. 对于普通的非替换行内元素(如 span),垂直外边距不会影响行布局。行内元素的内边距可以绘制而不会像预期那样推动相邻行。 当盒子需要独立的大小和间距时,请考虑使用 inline-block 或其他布局模式。
  3. 使用开发者工具检查选择器是否匹配,声明是否有效,以及是否有优先级更高的规则或后续简写覆盖了它。也要在窄宽度下进行比较,检查溢出情况以及内边距对宽度的影响。

要相对于书写方向定义间距,请使用逻辑属性,例如 padding-inline 和 margin-block。在普通的水平书写中,行内轴是水平的,块轴是垂直的。这些轴在垂直书写中会发生变化,因此它们并不总是对应于物理的左和右。