CSS 学习之正确看待 CSS 世界里的 margin 合并

news2025/1/6 18:33:03
一、什么是 margin 合并

块级元素的上外边距(margin-top)与下外边距(margin-bottom)有时会合并为单个外边距,这样的现象称为“margin 合并”。从此定义上,我们可以捕获两点重要的信息。

  1. 块级元素,但不包括浮动和绝对定位元素,尽管浮动和绝对定位可以让元素块状化。
  2. 只发生在垂直方向,需要注意的是,这种说法在不考虑 writing-mode 的情况下才是正确的,严格来讲,应该是只发生在和当前文档流方向的相垂直的方向上。由于默认文档流 是水平流,因此发生 margin 合并的就是垂直方向。
二、margin 合并的 3 种场景

margin 合并有以下 3 种场景。
(1) 相邻兄弟元素 margin 合并。这是 margin 合并中最常见、最基本的,例如:

p { margin: 1em 0; }
<p>第一行</p>
<p>第二行</p>

则第一行和第二行之间的间距还是 1em,因为第一行的 margin-bottom 和第二行的 margin-top 合并在一起了,并非上下相加。

(2) 父级和第一个/最后一个子元素。我们直接看例子,在默认状态下,下面 3 种设置是等 效的:

<div class="father">
  <div class="son" style="margin-top: 80px"></div>
</div>

<div class="father" style="margin-top: 80px">
  <div class="son"></div>
</div>

<div class="father" style="margin-top: 80px">
  <div class="son" style="margin-top: 80px"></div>
</div>

在实际开发的时候,给我们带来麻烦的多半就是这里的父子 margin 合并。 比方说,现在流行官网使用一张帅帅的大图,然后配上大大的网站标

题。由于这个标题一般在头图中间的某位置,因此,我们很自然会想到使用 margin-top 定位,然后问题就来了。因为发生了“奇怪”的事情,头图居然 掉下来了!针对此现象,我特意制作了一个实例。

<div class="container">
    <h2>CSS世界</h2>
</div>
.container {
    max-width: 1920px;
    height: 384px;
    background: url(cover.jpg) no-repeat center;
}
.container > h2 {
    font-size: 128px;
    margin-top: 100px;
    color: #fff;
}

请添加图片描述

问题产生的原因就是这里的父子 margin 合并。这里大家需要理清楚“合并”这个概念。 如果我们按照中文释义理解,应该必须有多个对象才能进行合并,否则根本就没有“合”这一说,确实如此。但是,这样理解也有可能会带来这样一个误区,即你要出点儿力,我要出点儿 力,才叫“合”,其实不然。放到我们这里,这个父子 margin 合并的案例上就是:父元素没有出一点力,子元素出了全部的力,然后最终的 margin 全部合到了父元素上。也就是虽然是在子元素上设置的 margin-top,但实际上就等同于在父元素上设置了 margin-top,我想这样大家就能理解为何头图会掉下来了吧。但是有一点需要注意,“等同于”并不是“就是”的意思,我们使用 getComputedStyle 方法获取父元素的 margin-top 值还是 CSS 属性中设置值, 并非 margin 合并的表现值。

那该如何阻止这里 margin 合并的发生呢?

对于 margin-top 合并,可以进行如下操作(满足一个条件即可):

  • 父元素设置为块状格式化上下文元素;
  • 父元素设置 border-top 值;
  • 父元素设置 padding-top 值;
  • 父元素和第一个子元素之间添加内联元素进行分隔。

对于 margin-bottom 合并,可以进行如下操作(满足一个条件即可):

  • 父元素设置为块状格式化上下文元素;

  • 父元素设置 border-bottom 值;

  • 父元素设置 padding-bottom 值;

  • 父元素和最后一个子元素之间添加内联元素进行分隔;

  • 父元素设置 height、min-height 或 max-height。

    所以,上面因为 margin 合并导致头图掉下来的问题可以添加下面的 CSS 代码进行

    修复:

  .container {
    overflow: hidden;
  }

其原理就是通过设置 overflow 属性让父级元素块状格式化上下文,这在 6.4 节会有深入的 探讨。

说到此处,忍不住再多说几句。jQuery 中有个 ( ) . s l i d e U p ( ) / ().slideUp()/ ().slideUp()/().slideDown()方法, 如果在使用这个动画效果的时候,发现这内容在动画开始或结束的时候会跳一下,那八九不离十就是布局存在margin合并。跳动之所以产生,就是因为jQuery的slideUp和slideDown 方法在执行的时候会被对象元素添加 overflow:hidden 设置,而 overflow: hidden 会阻 止 margin 合并,于是一瞬间间距变大,产生了跳动。

(3) 空块级元素的 margin 合并。例如,下面 CSS 和 HTML 代码:

.father { overflow: hidden; }
.son { margin: 1em 0; }
<div class="father">
   <div class="son"></div>
</div>

结果,此时.father 所在的这个父级<div>元素高度仅仅是 1em,因为.son 这个空<div>元 素的 margin-top 和 margin-bottom 合并在一起了。这也是上一节 margin:50%最终宽高 比是 2:1 的原因,因为垂直方向的上下 margin 值合二为一了,所以垂直方向的外部尺寸只有 水平方向的一半。

这种空块级元素的 margin 合并特性即使自身没有设置 margin 也是会发生的,所谓“合” 并不一定要自己出力,只要出人就可以。比方说,我们一开始的“相邻兄弟元素 margin 合并”, 其实,就算兄弟不相邻,也是可以发生合并的,前提是中间插手的也是个会合并的家伙。比方说:

p { margin: 1em 0; }
<p>第一行</p>
<div></div> 
<p>第二行</p>

此时第一行和第二行之间的距离还是 1em,中间看上去隔了一个

元素,但对最终效果却没有任何影响。如果非要细究,则实际上这里发生了 3 次 margin 合并, <div>和第一行 <p> 的 margin-bottom 合并,然后和第二行 <p>的 margin-top 合并,这两次合并是相邻兄弟合并。由于自身是空 <div>,于是前两次合并的 margin-bottom 和 margin-top 再次合并, 这次合并是空块级元素合并,于是最终间距还是 1em。

根据我多年开发的经验,由于空块级元素的 margin 合并发生不愉快事情的情况非常之少。 一来,我们很少会在页面上放置没什么用的空<div>;二来,即使使用空<div>也是画画分隔 线之类的,一般都是使用 border 属性,正好可以阻断 margin 合并;三来,CSS 开发人员普遍没有 margin 上下同时开工的习惯,比方说一个列表,间距都是一样的,开发人员一般都是单独设定 margin-top 或 margin-bottom 值,因为这会让他们内心觉得更安全。于是,最终,空块级元素的 margin 合并就变成了一个对 CSS 世界有着具有巨大意义但大多数人都不知道的特性。

如果有人不希望空<div>元素有 margin 合并,可以进行如下操作:

  • 设置垂直方向的 border;
  • 设置垂直方向的 padding;
  • 里面添加内联元素(直接 Space 键空格是没用的);
  • 设置 height 或者 min-height。
三、margin 合并的计算规则

我把 margin 合并的计算规则总结为“正正取大值”“正负值相加”“负负最负值”3 句话。

下面来分别举例说明。

(1) 正正取大值。如果是相邻兄弟合并:

.a { margin-bottom: 50px; }
.b { margin-top: 20px; }
<div class="a"></a>
<div class="b"></a>

此时.a 和.b 两个<div>之间的间距是 50px,取大的那个值。 如果是父子合并:

.father { margin-top: 20px; }
.son { margin-top: 50px; }
<div class="father">
  <div class="son"></div>
</div>

此时.father 元素等同于设置了 margin-top:50px,取大的那个值。 如果是自身合并:

.a {
  margin-top: 20px;
  margin-bottom: 50px;
}
<div class="a"></div>

则此时.a 元素的外部尺寸是 50px,取大的那个值。

(2)正负值相加。如果是相邻兄弟合并:

.a { margin-bottom: 50px; }
.b { margin-top: -20px; }
<div class="a"></a>
<div class="b"></a>

此时.a 和.b 两个<div>之间的间距是 30px,是-20px+50px 的计算值。 如果是父子合并:

.father { margin-top: -20px; }
.son { margin-top: 50px; }
<div class="father">
  <div class="son"></div>
</div>

此时.father 元素等同于设置了 margin-top:30px,是-20px+50px 的计算值。 如果是自身合并:

.a {
  margin-top: -20px;
  margin-bottom: 50px;
}
<div class="a"></div>

则此时.a 元素的外部尺寸是 30px,是-20px+50px 的计算值。

(3) 负负最负值。如果是相邻兄弟合并:

.a { margin-bottom: -50px; }
.b { margin-top: -20px; }
<div class="a"></a>
<div class="b"></a>

此时.a 和.b 两个<div>之间的间距是-50px,取绝对负值最大的值。 如果是父子合并:

.father { margin-top: -20px; }
.son { margin-top: -50px; }
<div class="father">
 <div class="son"></div>
</div>

此时.father 元素等同于设置了 margin-top:-50px,取绝对负值最大的值。

如果是自身合并:

.a {
 margin-top: -20px;
 margin-bottom: -50px;
}
<div class="a"></div>

则此时.a 元素的外部尺寸是-50px,取绝对负值最大的值。

四、margin 合并的意义

我之前曾见到类似这样的说法:“margin-top 合并 bug。”这种说法是大有问题的,

“margin-top 合并”这种特性是故意这么设计的,在实际内容呈现的时候是有着重要意义的, 根本就不是 bug!不要遇到出乎自己意料或者自己无法理解的现象就称其为 bug。

CSS世界的CSS属性是为了更好地进行图文信息展示而设计的,博客文章或者新闻信息是图文信息的典型代表,基本上离不开下面这些 HTML:

<h2>文章标题</h2>
<p>文章段落 1...</p>
<p>文章段落 2...</p>
<ul>
  <li>列表 1</li>
  <li>列表 2</li>
  <li>列表 3</li>
</ul>

而这里的<h2>、<p>、<ul>默认全部都是有垂直方向的 margin 值的,而且单位全部都是 em。 首先解释一下为何需要 margin 值。其实原因很简单,CSS 世界的设计本意就是图文信息展示, 有了默认的 margin 值,我们的文章、新闻就不会挤在一起,垂直方向就会层次分明、段落有致,阅读体验就会好!为何使用 em 作为单位也很好理解,大家应该知道浏览器默认的字号大小是可以自定义的吧,例如,默认的是 16 像素,假如我们设置成更大号的字号,同时 HTML 标签的 margin 是像素大小,则会发生文字变大但是间距不变的情况,原本段落有致的阅读体验必然又会变得令人窒息。em 作为相对单位,则可以让我们的文章或新闻无论多大的字体都排版良好。可以看到,HTML 标签默认内置的 CSS 属性值完全就是为了更好地进行图文信息展示 而设计的。

我们平时进行网站开发的时候都会重置各种默认的 margin 尺寸,这是件需要好好审视的事情,对于绝大多数网站,确实需要做这样的处理,因为这些网站鲜有传统的图文信息展示区域。但是,如果你的站点是博客、新闻门户或公众号文章,我们应该做的是统一标签的 margin 大小,而不是一股脑地重置成 0。

下面说说 margin 合并的意义。对于兄弟元素的 margin 合并其作用和 em 类似,都是让图文信息的排版更加舒服自然。假如说没有 margin 合并这种说法,那么连续段落或列表之类首尾项间距会和其他兄弟标签成 1:2 关系;文章标题距离顶部会很近,而和下面的文章详情内容距离又会很开,就会造成内容上下间距不一致的情况。这些都是糟糕的排版体验。而合并机制可以保证元素上下间距一致,无论是<h2>标题这种 margin 偏大的元素,还是中规中矩的<p> 元素,因为“正正取大值”。

父子 margin 合并的意义在于:在页面中任何地方嵌套或直接放入任何裸<div>,都不会 影响原来的块状布局。<div>是网页布局中非常常用的一个元素,其语义是没有语义,也就是不代表任何特定类型的内容,是一个通用型的具有流体特性的容器,可以用来分组或分隔。由于其作用就是分组的,因此,从行为表现上来看,一个纯粹的<div>元素是不能够也不可以影响原先的布局的。现在有如下一段 HTML:

    <div style="margin-top:20px;"></div>

请问:现在要在上面这段 HTML 的外面再嵌套一层<div>元素,假如说现在没有父子 margin 合并,那这层新嵌套的<div>岂不阻断了原本的兄弟 margin 合并?很有可能间距就会变大, 妥妥地影响了原来的布局,这显然就违背了<div>的设计作用了。所以才有了父子 margin 合并,外面再嵌套一层<div>元素就跟没嵌套一样,表现为 margin-top:20px 就好像是设置在最外面的<div>元素上一样。

自身 margin 合并的意义在于可以避免不小心遗落或者生成的空标签影响排版和布局。 例如:

<p>第一行</p>
<p></p>
<p></p>
<p></p>
<p></p>
<p>第二行</p>

其和下面这段 HTML 最终视觉效果是一模一样的:

<p>第一行</p>
<p>第二行</p>

若是没有自身 margin 合并特性的话,怕是上面的 HTML 第一行和第二行之间要隔了很多行吧。

知道了 margin 合并的意义以及作用,而且合并规则的兼容性良好,所以,我自己平时网页制作的时候,遇到列表或者模块,全部都是保留上下 margin 设置。例如:

.list {
  margin-top: 15px;
  margin-bottom: 15px;
}

而不是战战兢兢地使用:

.list {
  margin-top: 15px;
}

因为 margin 合并特性,所以我们无须担心列表之间的间距会很大。不会的,就是 15px! 相反,这种设置让我们的页面结构容错性更强了,比方说最后一个元素移除或位置调换,均不会破坏原来的布局,也就是我们的 CSS 无须做任何调整。

参考资料: 《CSS 世界》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2271060.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Golang的代码质量分析工具

Golang的代码质量分析工具 一、介绍 作为一种高效、简洁、可靠的编程语言&#xff0c;被越来越多的开发者所喜爱和采用。而随着项目规模的增长和团队人员的扩大&#xff0c;代码质量的管理变得尤为重要。为了保障代码的可维护性、健壮性和可扩展性&#xff0c;我们需要借助代码…

鸿蒙元服务 口袋管家(从0到1) ——准备工作

达到的效果图 如何创建元服务&#xff1f; 如下&#xff1a; 鸿蒙如何创建元服务-元服务是什么&#xff1f;和App的关系&#xff1f;&#xff08;保姆级步骤&#xff09;_鸿蒙元服务-CSDN博客 开始创建包 Bill 里面创建两个page页面 分别是 BillAddPage 和 BillIndexPag…

轻量型web组态软件

体验地址&#xff1a;http://www.hcy-soft.com 随着互联网、物联网技术的快速发展&#xff0c;BY组态基于多年研发积累和私有部署实践打磨、以及对业务场景的深入理解&#xff0c;推出了适用于物联网应用场景的轻量型web组态软件。 该产品采用 B/S 架构&#xff0c;提供 web …

Linux C/C++编程-获得套接字地址、主机名称和主机信息

【图书推荐】《Linux C与C一线开发实践&#xff08;第2版&#xff09;》_linux c与c一线开发实践pdf-CSDN博客《Linux C与C一线开发实践&#xff08;第2版&#xff09;&#xff08;Linux技术丛书&#xff09;》(朱文伟&#xff0c;李建英)【摘要 书评 试读】- 京东图书 (jd.com…

SweetAlert2 - 漂亮可定制的 JavaScript 弹窗

https://sweetalert2.github.io/ https://github.com/sweetalert2/sweetalert2 安装&#xff1a; npm install sweetalert2封装&#xff1a; import Swal from sweetalert2/dist/sweetalert2.js import sweetalert2/src/sweetalert2.scss/*** * param {string} icon - ico…

Android布局layout的draw简洁clipPath实现圆角矩形布局,Kotlin

Android布局layout的draw简洁clipPath实现圆角矩形布局&#xff0c;Kotlin 通常&#xff0c;如果要把一个相对布局&#xff0c;FrameLayout&#xff0c;或者线性布局等这样的布局变成具有圆角或者圆形的布局&#xff0c;需要增加一个style&#xff0c;给它设置圆角&#xff0c;…

PHP如何删除数组中的特定值?

php 中删除数组特定值的方法有三种&#xff1a;unset()&#xff1a;直接删除指定索引的值&#xff0c;但会保留数组索引结构和未删除元素&#xff0c;适合小数组。array_filter()&#xff1a;根据自定义回调函数筛选数组元素&#xff0c;返回一个新数组&#xff0c;原数组不变&…

啤酒风味塑造的关键因素——麦汁煮沸

在探索啤酒酿造的工艺过程中&#xff0c;我们发现每一个细微的步骤都对最终的口感和风味产生着不可忽视的影响。今天&#xff0c;让我们深入探讨一个关键环节——麦汁煮沸&#xff0c;以及其中至关重要的概念“煮沸强度”。 何谓煮沸强度&#xff1f;它又如何左右麦汁的品质&a…

unity开发之shader 管道介质流动特效

效果 shader graph 如果出现下面的效果&#xff0c;那是因为你模型的问题&#xff0c;建模做贴图的时候没有设置好UV映射&#xff0c;只需重新设置下映射即可

JAVA学习笔记_JVM

文章目录 初识jvm内存结构程序计数器(寄存器) 栈问题辨析内存溢出 线程诊断本地方法栈Heap堆内存溢出内存诊断 方法区内存溢出常量池 stringTable直接内存垃圾回收 初识jvm JRE JVM 基础类库 JDK JRE 编译工具 JavaSE JDK IDE工具 JavaEE JDK 应用服务器 IDE工具 jvm是…

供需平台信息发布付费查看小程序系统开发方案

供需平台信息发布付费查看小程序系统主要是为了满足个人及企业用户的供需信息发布与匹配需求。 一、目标用户群体 个人用户&#xff1a;寻找兼职工作、二手物品交换、本地服务&#xff08;如家政、维修&#xff09;等。 小微企业&#xff1a;推广产品和服务&#xff0c;寻找合…

牛客网刷题 ——C语言初阶——OR76 两个整数二进制位不同个数

1. 牛客网题目&#xff1a;OR76 两个整数二进制位不同个数 牛客网OJ链接 描述&#xff1a; 输入两个整数&#xff0c;求两个整数二进制格式有多少个位不同 输入描述&#xff1a;两个整数 输出描述&#xff1a;二进制不同位的个数 示例1 输入&#xff1a;22 33 输出&#xff1a…

直播美颜SDK深度优化技术探索:低延迟与高画质的平衡之道

本篇文章&#xff0c;小编将从技术角度出发&#xff0c;探讨直播美颜SDK的优化方法&#xff0c;探索实现低延迟与高画质并存的解决方案。 一、低延迟的技术挑战与应对策略 直播的核心在于实时互动&#xff0c;任何超过100ms的延迟都会显著影响用户体验。而美颜处理由于涉及复…

链表算法篇——链接彼岸,流离节点的相遇之诗(下)

文章目录 前言第一章&#xff1a;重排链表1.1 题目链接&#xff1a;https://leetcode.cn/problems/reorder-list/description/1.2 题目分析&#xff1a;1.3 思路讲解&#xff1a;1.4 代码实现&#xff1a; 第二章&#xff1a;合并K个升序链表2.1 题目链接&#xff1a;https://l…

WebRTC的线程事件处理

1. 不同平台下处理事件的API&#xff1a; Linux系统下&#xff0c;处理事件的API是epoll或者select&#xff1b;Windows系统下&#xff0c;处理事件的API是WSAEventSelect&#xff0c;完全端口&#xff1b;Mac系统下&#xff0c;kqueue 2. WebRTC下的事件处理类&#xff1a; …

【Cocos TypeScript 零基础 4.1】

目录 背景滚动 背景滚动 创建一个 空节点 背景丟进去 ( 复制一个,再丢一次都行) 新建TS脚本 并绑定到 空节点 上 再对TS脚本进行编辑 export class TS2bg extends Component {property (Node) // 通过属性面板去赋值bg1:Node nullproperty (Node) bg2:Node nullprope…

利用 AI 高效生成思维导图的简单实用方法

#工作记录 适用于不支持直接生成思维导图的AI工具&#xff1b;适用于AI生成后不能再次编辑的思维导图。 在日常的学习、工作以及知识整理过程中&#xff0c;思维导图是一种非常实用的工具&#xff0c;能够帮助我们清晰地梳理思路、归纳要点。而借助 AI 的强大能力&#xff0c…

AfuseKt1.4.4 | 刮削视频播放器,支持阿里云盘和自动海报墙

AfuseKt是一款功能强大的安卓端在线视频播放器&#xff0c;广泛兼容多种平台如阿里云盘、Alist、WebDAV、Emby、Jellyfin等&#xff0c;同时也支持本地存储视频文件的播放。其特色功能包括自动抓取影片信息生成海报墙展示&#xff0c;充分利用设备硬件进行高清视频流畅播放&…

Linux下部署ElasticSearch集群

Elasticsearch7.17.8集群的搭建 节点host名称节点ip节点部署内容k8s-m192.168.40.142主节点 数据节点k8s-w1192.168.40.141主节点 数据节点k8s-w2192.168.40.140数据节点 一、准备安装环境 1.下载安装包 官网 www.elastic.co 下载所有版本地址 点击跳转 下载elasticsearch-7.…

covid-vaccine-availability-using-flask-server

使用烧瓶服务器获得 Covid 疫苗 原文:https://www . geesforgeks . org/co vid-疫苗-可用性-使用-烧瓶-服务器/ 在本文中&#xff0c;我们将使用 Flask Server 构建 Covid 疫苗可用性检查器。 我们都知道&#xff0c;整个世界都在遭受疫情病毒的折磨&#xff0c;唯一能帮助我们…