CSS探索系列之Margin

外边距(margin)位于元素边框外侧,用来分隔当前盒子与周边其他盒子。其他盒子不会侵入外边距区域,父元素背景可以透过外边距区域显示出来。

作用:控制盒子与周边元素的间距,实现留白、居中;同时具备几个特殊行为:块级元素会发生外边距折叠、百分比取值参照包含块行内尺寸、auto 关键字分配剩余空间。

取值与简写

margin 可拆分为四个独立方向属性:margin-topmargin-rightmargin-bottommargin-left;也支持复合简写。

可用取值:

  • 长度值pxremem 等;支持负值,负值会让相邻元素互相靠近,甚至发生重叠。
  • 百分比:相对于包含块的行内尺寸计算。
  • auto:行内方向可自动分配剩余空间;普通流下块向会解析为 0

简写规则:顺序「上 → 右 → 下 → 左」,支持 1~4 个值自动推导补全剩余方向。

外边距折叠

外边距折叠(margin collapsing):同一块级格式化上下文(BFC)中,普通流块级盒子会发生垂直外边距合并,最终只保留一个外边距。

水平书写模式下,仅 margin-topmargin-bottom 参与折叠;竖排书写模式,折叠行为切换到行方向。

合并计算规则

  • 两边均为正值:取更大的值;
  • 一正一负:两值直接相加;
  • 两边均为负值:取更负(绝对值更大)的值。

折叠必要条件

  • 参与折叠的是普通流块级盒子
  • 两个外边距之间无分隔:不存在 borderpadding、内容、行框阻断;
  • 不参与折叠:浮动元素、绝对定位元素、Flex子项、Grid子项。

四类折叠场景

  • 父子顶部折叠:父 margin-top 与第一个普通流子元素 margin-top 合并;
  • 兄弟相邻折叠:前一个兄弟的 margin-bottom 与后一个兄弟的 margin-top 合并;
  • 父子底部折叠:高度为 auto 的父容器 margin-bottom,与最后一个普通流子元素 margin-bottom 合并;
  • 空盒子自折叠:盒子满足 height:0|automin-height:0、无普通流子元素、没有新建 BFC,自身 margin-topmargin-bottom 互相合并。

示例:两个相邻段落设置 margin:50px 0,中间间距不是 50+50=100px,而是折叠后 50px

<div style="border:1px solid #f00;">
    <p style="margin:50px 0;">我喜欢回忆,是因为回忆是我们审视自己生活的过滤器。</p>
    <p style="margin:50px 0;">回忆模糊不清,就给自我欺骗提供了机会。</p>
</div>

阻止外边距折叠的手段

  • 根元素的 margin 不会参与折叠;
  • 创建新 BFC,阻断父子折叠(兄弟仍在同一父级 BFC 中);
  • 父容器增加 borderpadding,隔断父子 margin 接触;
  • 父容器设置非 autoheight,避免和最后子元素折叠;min-heightmax-height 挡不住这一类;
  • 空盒子设置 heightmin-height,阻止自身上下 margin 折叠。

margin 百分比

所有方向的 margin 百分比,统一参照包含块的行内尺寸

  • 默认横排(水平书写):行内尺寸 = 包含块宽度;哪怕是 margin-topmargin-bottom,百分比依旧参照宽度,不是父容器高度
  • 竖排书写模式:行内尺寸 = 包含块高度。

设计原因:主要为规避普通流下的循环布局依赖;统一全部方向参照行内尺寸是规范方案。

示例:父容器宽 600px、高 300px;子元素设置 margin:10% 5%

计算结果:上下margin = 600*10% = 60px,左右margin = 600*5% =30px → 最终 margin:60px 30px 60px 30px不是参照父高度得到 15px

<div style="width:600px;height:300px;border:1px solid #f00;">
    <p style="margin:10% 5%;background:#0aa;">
        我喜欢回忆,是因为回忆是我们审视自己生活的过滤器。
    </p>
</div>

margin 的 auto

普通流块级盒子:块向(横排下即上下)的 margin-top:automargin-bottom:auto 计算值为 0margin: auto 等价于 margin:0 auto,仅实现水平居中。该规则对绝对定位、Flex、Grid子项不生效。

行内方向(横排下左右)的 auto 会分配剩余空间。

规则总结

  • 普通流块盒子必须设置确定宽度,左右 auto 才有剩余空间可分配;width: auto 时盒子直接撑满父容器,无余量。
  • 非替换行内盒子,左右 auto 解析为0,无法用 margin:0 auto 居中。
  • 绝对定位:对应轴的两个 inset 都设置(水平居中要 leftright)且该轴有确定尺寸,margin:auto 可在该轴居中。
  • Flex 子项、Grid子项:四个方向 auto 都可以瓜分剩余空间;单个子元素下写 margin: auto 可实现完全居中。

示例:父宽 600px,子定宽 200px,设置 margin-left:auto。剩余空间全部分配给左侧,实现右对齐。 image-20200503234104813

<div style="width:600px; border:1px solid #f00;">
    <div style="width:200px; margin-left:auto; background:#0aa;">
        我喜欢回忆,是因为回忆是我们审视自己生活的过滤器。回忆模糊不清,就给自我欺骗提供了机会。—— 石黑一雄《远山淡影》
    </div>
</div>

margin 与相对定位偏移的区别

margin-top: 30pxposition:relative; top: 30px 视觉上都可以让盒子向下偏移 30px,但布局行为完全不同:

  • margin:参与普通流布局。会改变盒子占位,后面兄弟元素跟随偏移后的位置继续排布。
  • relative + top:只改变盒子渲染绘制位置。元素原本占位保持不变,后续兄弟依旧在原始位置排布。

image-20260817234034516

image-20260817234110357

section { padding: 20px; border: 1px solid #f00; }
.item { display: inline-block; width: 48%; vertical-align: top; }
<section>
    <div class="item">
        <div style=" margin-top: 30px; color: #a0a; border: 1px solid #a0a;">
            我喜欢回忆,是因为回忆是我们审视自己生活的过滤器。回忆模糊不清,就给自我欺骗提供了机会。—— 石黑一雄《远山淡影》
        </div>
        人常常靠着改编过的过去获得心灵平静。
    </div>
    <div class="item">
        <div style="position: relative; top: 30px; color: #0aa; border: 1px solid #0aa;">
            我喜欢回忆,是因为回忆是我们审视自己生活的过滤器。回忆模糊不清,就给自我欺骗提供了机会。—— 石黑一雄《远山淡影》
        </div>
        人常常靠着改编过的过去获得心灵平静。
    </div>
</section>

书写模式对 margin 的影响

margin 的折叠、百分比、auto 逻辑,跟随行向、块向,而不是固定的上下左右物理方向。

  • 水平书写 horizontal-tb(默认)
    • 折叠发生在垂直物理方向;
    • 百分比参照包含块宽度;
    • 块向(上下)auto = 0;margin:auto 等价 margin:0 auto
  • 垂直书写 vertical-rlvertical-lr
    • 折叠发生在水平物理方向;
    • 百分比参照包含块高度(此时高度为行内尺寸);
    • 块向(物理左右)auto = 0;margin:auto 等价 margin:auto 0
.h { writing-mode: horizontal-tb; }
.v { writing-mode: vertical-rl; }

圣杯布局

圣杯布局:两侧定宽、中间自适应。

实现:中间 DOM 靠前优先渲染;全部浮动;利用负 margin 把侧边栏拉回可视区域,再用 relative 偏移到父容器 padding 预留的位置。

image-20260817234904408

<section>
    <header>header</header>
    <div class="container">
        <div class="middle">middle(中间弹性区)</div>
        <div class="left">left</div>
        <div class="right">right</div>
    </div>
    <footer>footer</footer>
</section>
<style>

    header, footer, .middle, .left, .right {
        border: 1px solid #0aa;
        padding: 15px 10px;
        text-align: center;
        box-sizing: border-box;
    }

    .container {
        overflow: hidden;
        padding: 0 200px;
    }

    .middle {
        width: 100%;
        float: left;
    }

    .left {
        width: 200px;
        float: left;
        margin-left: -100%;
        position: relative;
        left: -200px;
    }

    .right {
        width: 200px;
        float: left;
        margin-left: -200px;
        position: relative;
        right: -200px;
    }
</style>

双飞翼布局

双飞翼布局:两侧定宽、中间自适应。

实现:不使用 relative 偏移,中间栏内部嵌套一层,靠内层 padding 留出侧边栏位置;侧边栏仅使用负 margin 完成定位。

<section>
    <header>header</header>
    <div class="container">
        <div class="middle">
            <div class="in">middle(中间弹性区)</div>
        </div>
        <div class="left">left</div>
        <div class="right">right</div>
    </div>
    <footer>footer</footer>
</section>
<style>
    header, footer, .middle, .left, .right {
        padding: 15px 10px;
        border: 1px solid #0aa;
        box-sizing: border-box;
        text-align: center;
    }

    .container {
        overflow: hidden;
    }

    .middle {
        width: 100%;
        float: left;
    }

    .in {
        padding: 0 200px;
    }

    .left {
        width: 200px;
        float: left;
        margin-left: -100%;
    }

    .right {
        width: 200px;
        float: left;
        margin-left: -200px;
    }
</style>

margin 视觉无效的几种场景

  • 外边距折叠:外边距发生合并,修改数值较小一侧,视觉看不出变化。
  • 行内非替换元素:垂直 margin 不参与行高计算,视觉无效;水平 margin 正常生效。替换元素,如:<img> 垂直 margin 有效,也不参与折叠。
  • 表格盒模型trtddisplay:table-rowtable-cell 不支持 margintableinline-tabletable-caption 可以正常使用 margin
  • 绝对定位元素:没有全部设置对应方向 inset 时,对应方向 margin 不参与剩余空间分配。四个方向 inset 都设置且盒子有确定宽高,margin:auto 才可以完全居中。
  • 父容器定高、定宽:父设置固定高度,不会被子元素 margin-bottom 撑开;定宽同理,margin-right 撑不开父宽度。margin 会溢出父容器,看起来像失效,实际计算存在。

小结

margin 控制盒子与周边元素的布局关系:

  • 普通流块级盒子会发生外边距折叠;
  • 百分比统一参照包含块行内尺寸,受书写模式影响;
  • 普通流下块向 auto 解析为0;
  • 需要推动周边盒子位置使用 margin;只想移动自身、不影响后续布局,选择相对定位偏移。

© lizhao all right reserved,powered by Gitbook文件修订时间: 2026-08-18 22:07:38

results matching ""

    No results matching ""