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

一、什么是 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.rhkb.cn/news/502993.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

小程序组件 —— 28 组件案例 - 推荐商品区域 - 实现结构样式

这一节目标是实现底部推荐商品的结构和样式&#xff0c;由于这里要求横向滚动&#xff0c;所以需要使用上节介绍的 scroll-view 功能&#xff0c;并使用 scroll-x 属性支持横向滚动&#xff0c;推荐商品区域中的每一个商品是一个单独的 view&#xff0c;每个view 中需要写三个组…

单片机-LED点阵实验

要将第一个点点亮&#xff0c;则 1 脚接高电平 a 脚接低电平&#xff0c;则第一个点就亮了&#xff1b;如果要将第一行点亮&#xff0c;则第 1 脚要接高电平&#xff0c;而&#xff08;a、b、c、d、e、f、g、h &#xff09;这些引脚接低电平&#xff0c;那么第一行就会点亮&…

软件项目体系建设文档,项目开发实施运维,审计,安全体系建设,验收交付,售前资料(word原件)

软件系统实施标准化流程设计至关重要&#xff0c;因为它能确保开发、测试、部署及维护等各阶段高效有序进行。标准化流程能减少人为错误&#xff0c;提升代码质量和系统稳定性。同时&#xff0c;它促进了团队成员间的沟通与协作&#xff0c;确保项目按时交付。此外&#xff0c;…

Java基础 注解

分类 Java自带的标准注解&#xff0c;包括Override、Deprecated和SuppressWarnings&#xff0c;分别用于标明重写某个方法、标明某个类或方法过时、标明要忽略的警告&#xff0c;用这些注解标明后编译器就会进行检查。元注解&#xff0c;元注解是用于定义注解的注解&#xff0…

Linux中rsync命令使用

一、rsync简介 rsync 是一种高效的文件复制和同步工具&#xff0c;常用于在本地或远程计算机之间同步文件和目录 主要特性增量同步&#xff1a;rsync 会检测源和目标文件之间的差异&#xff0c;只传输发生变化的部分&#xff0c;而不是重新传输整个文件。这样就能有效减少数据…

基于STM32的自动水满报警系统设计

目录 引言系统设计 硬件设计软件设计系统功能模块 水位检测模块报警模块自动控制模块控制算法 水位检测逻辑报警触发逻辑代码实现 水位检测模块报警控制模块自动控制逻辑系统调试与优化结论与展望 1. 引言 水满报警系统在家庭、农业、工业等领域广泛应用&#xff0c;通过实时…

【Java数据结构】二叉树

1.树型结构 1.1树的概念 树是一种非线性的数据结构&#xff0c;由n个结点组成的具有层次关系的集合。下面是它的特点&#xff1a; 根结点是没有前驱的结点&#xff08;没有父结点的结点&#xff09;子结点之间互不相交除了根结点外&#xff0c;其它结点都只有一个父结点n个结…

学习threejs,导入AWD格式的模型

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.AWDLoader AWD模型加…

Chapter4.3:Implementing a feed forward network with GELU activations

4 Implementing a GPT model from Scratch To Generate Text 4.3 Implementing a feed forward network with GELU activations 本节即将实现子模块&#xff0c;用于transformer block&#xff08;变换器块&#xff09;的一部分。为此&#xff0c;我们需要从激活函数开始。 深…

弥散张量分析开源软件 DSI Studio 简体中文汉化版可以下载了

网址&#xff1a; (63条消息) DSIStudio简体中文汉化版(2022年7月)-算法与数据结构文档类资源-CSDN文库

【信号滤波 (补充)】二阶陷波滤波代码推导过程(C++)

目录 二阶陷波滤波器计算实例一、 传递函数的参数推导1. 首先 b 0 , b 1 , b 2 b_0, b_1, b_2 b0​,b1​,b2​是怎么推导出来的&#xff1f;2. 带入实际值求解3. 验证上述的传递函数 二、将传递函数转化成差分方程2.1 传递函数写成输入输出形式2.2 Z域转化为时域 三、将差分方程…

C++进阶——用Hash封装unordered_map和unordered_set

目录 前言 源码怎么说 为什么要兼容&#xff1f; 兼容的具体做法&#xff1f; 为什么要把Key转为整数&#xff08;HashFcn&#xff09;&#xff1f; 模拟实现 一、建立框架 二、迭代器 运算符重载 迭代器兼容大法 三、[ ]重载 四、实现不能修改key 实现及测试代码 …

安装MySQL的五种方法(Linux系统和Windows系统)

一.在Linux系统中安装MySQL 第一种方法:在线YUM仓库 首先打开MySQL官网首页 www.mysql.com 找到【DOWNLOADS】选项&#xff0c;点击 下拉&#xff0c;找到 【MySQL Community(GPL) Downloads】 在社区版下载页面中&#xff0c;【 MySQL Yum Repository 】链接为在线仓库安装…

极客说|微软 Phi 系列小模型和多模态小模型

作者&#xff1a;胡平 - 微软云人工智能高级专家 「极客说」 是一档专注 AI 时代开发者分享的专栏&#xff0c;我们邀请来自微软以及技术社区专家&#xff0c;带来最前沿的技术干货与实践经验。在这里&#xff0c;您将看到深度教程、最佳实践和创新解决方案。关注「极客说」&am…

封装/前线修饰符/Idea项目结构/package/impore

目录 1. 封装的情景引入 2. 封装的体现 3. 权限修饰符 4. Idea 项目结构 5. package 关键字 6. import 关键字 7. 练习 程序设计&#xff1a;高内聚&#xff0c;低耦合&#xff1b; 高内聚&#xff1a;将类的内部操作“隐藏”起来&#xff0c;不需要外界干涉&#xff1b…

【C++】P5733 【深基6.例1】自动修正

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;题目描述&#x1f4af;解题思路概述&#x1f4af;第一种实现方式&#xff1a;直接使用字符ASCII值计算代码实现代码分析 &#x1f4af;第二种实现方式&#xff1a;直接修改…

【Elasticsearch】文档操作:添加、更新和删除

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编…

【insert 插入数据语法合集】.NET开源ORM框架 SqlSugar 系列

系列文章目录 &#x1f380;&#x1f380;&#x1f380; .NET开源 ORM 框架 SqlSugar 系列 &#x1f380;&#x1f380;&#x1f380; 文章目录 系列文章目录一、前言 &#x1f343;二、插入方式 &#x1f4af;2.1 单条插入实体2.2 批量 插入实体2.3 根据字典插入2.4 根据 Dat…

权限掩码umask

1 、 设置新建文件或目录的默认权限 在 Linux 系统中&#xff0c;当用户创建一个新的文件或目录时&#xff0c;系统都会为新建的文件或目录分配默认的权限&#xff0c;该默认权限与umask 值有关&#xff0c;其具体关系是&#xff1a; 新建文件的默认权限 0666-umask 值 新建…

202-01-06 Unity 使用 Tip1 —— UnityHub 模块卸载重装

文章目录 1 卸载模块2 更新配置文件3 重启 UnityHub 起因&#xff1a; ​ WebGL 平台打包程序报错&#xff0c;懒得修复了&#xff0c;因此粗暴地删了重装。但是 UnityHub 不支持卸载模块&#xff0c;因此手动配置。 1 卸载模块 ​ 以 Unity 6000.0.26f1c1 为例&#xff0c;其…