第五篇、盒子模型

1 阅读7分钟

核心概念

每个 HTML 元素的盒子由 4 个部分组成(从内到外):

  1. 内容区(content) :元素的实际内容(文字 / 图片等),对应width/height属性;
  2. 内边距(padding) :内容区和边框之间的空白,不会显示背景色 / 背景图(但会继承元素背景);
  3. 边框(border) :包裹内边距和内容的线条,可设置宽度、样式、颜色;
  4. 外边距(margin) :盒子与其他盒子之间的空白,完全透明,不会影响盒子自身尺寸。
  • 边框与轮廓的区别
    • 边框(border):占据布局空间,会影响元素的总尺寸。
    • 轮廓(outline):不占据布局空间,绘制在边框之外,常用于焦点状态提示。

box-sizing

盒子模型.png

外边距折叠

  • 适用场景

    1. 并列关系(兄弟) :上下相邻的块级元素,其下外边距与上外边距会发生合并。
    2. 嵌套关系(父子) :父元素与子元素无内边距 / 边框隔断时,各自的上 / 下外边距会发生合并(该场景也被称为「外边距塌陷」)。
    3. 空元素自合并:空块级元素(无内容、无 border、无 padding),其自身的上外边距与下外边距会发生合并,且可继续与相邻元素外边距合并。
  • 合并规则: 多个垂直外边距相遇时,会合并为一个外边距,其大小等于所有参与合并的外边距中的最大值;若包含负边距,则按以下规则计算:

    • 一正一负:最终间距 = 正边距 - 负边距的绝对值
    • 两个负边距:最终间距 = 绝对值更大的那个负值
  • 示例 1:兄弟元素合并

    • A 盒子设置 margin-bottom: 100px
    • B 盒子设置 margin-top: 50px
    • 合并后,两盒之间的实际间距为 100px,而非 150px。
  • 示例 2:父子元素塌陷(合并)

    • 父元素设置 margin-top: 20px
    • 子元素设置 margin-top: 10px
    • 合并后,整体表现为父元素的 margin-top: 20px,子元素的外边距 “穿透” 到父元素外部(即外边距塌陷)。
  • 示例 3:空元素自合并

    • 空元素设置 margin-top: 150px + margin-bottom: 150px
    • 合并后,实际只表现为 150px 的外边距;若与其他元素相邻,会继续参与合并。
  • 设计意义: 外边距合并是浏览器为保证排版一致性的设计,以段落文本为例,合并后段落之间的间距与页面顶部间距保持一致,避免出现 “段落间距是顶部两倍” 的不均匀效果。

  • 开发建议: 这是浏览器的固有特性,在布局时只需为其中一个元素设置所需的垂直外边距即可,避免重复设置导致预期混乱;若需彻底避免,可通过触发 BFC 实现。

  • 完整代码示例(包含兄弟 / 父子 / 空元素合并)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>外边距折叠 完整示例</title>
    <style>
        /* 兄弟元素合并 */
        .box-a {
            width: 200px;
            height: 100px;
            background: #ff6347;
            margin-bottom: 100px;
        }
        .box-b {
            width: 200px;
            height: 100px;
            background: #4169e1;
            margin-top: 50px;
        }
​
        /* 父子元素塌陷(合并) */
        .parent {
            width: 300px;
            background: #f0e68c;
            margin-top: 20px;
        }
        .child {
            width: 150px;
            height: 150px;
            background: #ff6347;
            margin-top: 10px; /* 与父元素margin-top合并,触发塌陷 */
        }
​
        /* 空元素自合并 */
        .empty {
            margin-top: 150px;
            margin-bottom: 150px;
        }
    </style>
</head>
<body>
    <!-- 兄弟元素合并 -->
    <div class="box-a">A盒子(margin-bottom: 100px)</div>
    <div class="box-b">B盒子(margin-top: 50px)</div>
    <hr><!-- 父子元素塌陷(合并) -->
    <div class="parent">
        <div class="child">子盒子(margin-top: 10px)</div>
    </div>
    <hr><!-- 空元素自合并 -->
    <div class="box-a">上盒子</div>
    <div class="empty"></div> <!-- 空元素,上下margin合并为150px,遇到其他元素相邻,会继续参与合并,如上边有个margin-bottom为100px的盒子,再次合并为150px -->
    <div style="width:150px;height:150px;background-color:pink;">下盒子</div>
</body>
</html>

外边距塌陷

margin-top 塌陷

  • 适用场景:嵌套关系(父子)的块级元素,子元素设置 margin-top 时,会导致父盒子整体向下移动。

  • 本质原因:父元素与子元素的 margin-top 发生合并,子元素的 margin-top“穿透” 到父元素外部,表现为父盒子整体位移。

  • 具体表现

    • 预期:子盒子在父盒子内向下偏移 50px
    • 实际:父盒子整体向下偏移 50px,子盒子在父盒子内仍紧贴顶部
  • 触发条件

    1. 父元素无 border-top / padding-top(无物理隔断)
    2. 父元素未触发 BFC(无 overflow: hidden / float / position: absolute 等)
    3. 子元素为块级元素,且设置了 margin-top
  • 代码示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>margin-top 塌陷</title>
    <style>
        .parent-top {
            width: 300px;
            height: 300px;
            background: #f0e68c;
            /* 无 border/padding/overflow,满足塌陷条件 */
        }
        .child-top {
            width: 150px;
            height: 150px;
            background: #ff6347;
            margin-top: 50px; /* 触发 top 塌陷 */
        }
    </style>
</head>
<body>
    <div class="parent-top">
        <div class="child-top">子盒子(top 塌陷)</div>
    </div>
</body>
</html>

margin-bottom 塌陷

  • 适用场景:嵌套关系(父子)的块级元素,子元素设置 margin-bottom 时,会导致父盒子高度未包含该外边距,且该外边距作用于父盒子与外部元素的间距。

  • 本质原因:父元素与子元素的 margin-bottom 发生合并,子元素的 margin-bottom“穿透” 到父元素外部,表现为父盒子高度未被撑开、间距外移。

  • 具体表现

    • 预期:父盒子高度 = 子盒子高度 + 子盒子 margin-bottom(150px + 50px = 200px)
    • 实际:父盒子高度 = 子盒子高度(150px),margin-bottom 作用于父盒子与下方元素的间距
  • 触发条件

    1. 父元素无 border-bottom / padding-bottom(无物理隔断)
    2. 父元素未触发 BFC(无 overflow: hidden / float / position: absolute 等)
    3. 父元素高度由内容撑开,且内部仅包含该子元素
    4. 子元素为块级元素,且设置了 margin-bottom
  • 代码示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>margin-bottom 塌陷</title>
    <style>
        .parent-bottom {
            width: 300px;
            background: #f0e68c;
            /* 无 height/border/padding/overflow,满足塌陷条件 */
        }
        .child-bottom {
            width: 150px;
            height: 150px;
            background: #ff6347;
            margin-bottom: 50px; /* 触发 bottom 塌陷 */
        }
        .rel {
            width: 150px;
            height: 150px;
            background: pink; /* 参考块,用于观察塌陷间距 */
        }
    </style>
</head>
<body>
    <div class="parent-bottom">
        <div class="child-bottom">子盒子(bottom 塌陷)</div>
    </div>
    <div class="rel">参考块</div>
</body>
</html>

通用解决方案

  1. 物理隔断:给父盒子添加对应方向的边框(如 border-top / border-bottom),物理上隔开父子元素的外边距。
  2. 缓冲层:给父盒子添加对应方向的内边距(如 padding-top / padding-bottom),原理与添加边框类似。
  3. 触发 BFC(推荐) :给父盒子添加 overflow: hidden; 属性,触发 BFC(块级格式化上下文),阻止外边距合并。
  • 修复代码示例
/* 以 margin-top 塌陷为例,bottom 塌陷修复方式一致 */
.parent-top {
    width: 300px;
    height: 300px;
    background: #f0e68c;
    overflow: hidden; /* 触发 BFC,修复塌陷 */
}

核心注意点

  • 仅垂直方向生效:水平方向的 margin-left / margin-right 永远不会发生折叠 / 塌陷。
  • 仅普通文档流块级元素生效:行内元素、浮动元素、绝对定位元素、Flex/Grid 子元素的外边距不会合并。
  • 空元素自合并:空块级元素的上下外边距会合并,若与其他元素相邻,会继续参与合并,这是段落元素占用空间较小的原因。
  • 实战修复优先用 BFCoverflow: hidden(或 display: flow-root)是无视觉副作用的最优修复方案,避免 border/padding 带来的尺寸干扰。

最终总结

外边距合并(折叠)是普通文档流中块级元素垂直外边距的合并行为,「外边距塌陷」是父子嵌套场景下的特殊合并表现,核心规则是「合并后取最大外边距」,其设计目的是保证排版均匀,实战中通过触发 BFC 即可彻底避免。