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.mzph.cn/diannao/66281.shtml

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

相关文章

【git】git stash相关指令

目录 git stashgit stash save “”git stash list&#xff1a; 获取stash列表git stash pop&#xff1a;恢复最近一次stash缓存git stash apply stash{index}: 恢复指定缓存在这里插入图片描述git stash drop stash{1}&#xff1a;删除指定缓存 git stash clear :删除stash gi…

用公网服务代理到本地电脑笔记

参考&#xff1a; 利用frp 穿透到内网的http/https网站&#xff0c;实现对外开放&#xff08;这篇博客有点老&#xff0c;需要改动&#xff0c;不能照抄&#xff09;&#xff1a;https://www.cnblogs.com/hahaha111122222/p/8509150.html frp内网穿透(windows和服务器)&#xf…

uni-app:实现普通选择器,时间选择器,日期选择器,多列选择器

效果 选择前效果 1、时间选择器 2、日期选择器 3、普通选择器 4、多列选择器 选择后效果 代码 <template><!-- 时间选择器 --><view class"line"><view classitem1><view classleft>时间</view><view class"right&quo…

GAN对抗生成网络(二)——算法及Python实现

1 算法步骤 上一篇提到的GAN的最优化问题是&#xff0c;本文记录如何求解这一问题。 首先为了表示方便&#xff0c;记&#xff0c;这里让最大的可视作常量。 第一步&#xff0c;给定初始的&#xff0c;使用梯度上升找到 ,最大化。关于梯度下降&#xff0c;可以参考笔者另一篇…

[读书日志]从零开始学习Chisel 第二篇:Scala的变量与函数(敏捷硬件开发语言Chisel与数字系统设计)

第一篇https://blog.csdn.net/m0_74021449/article/details/144887921 2.2 Scala的变量及函数 2.2.1变量定义与基本类型 变量声明 变量首次定义必须使用关键字var或者val&#xff0c;二者的区别是val修饰的变量禁止被重新赋值&#xff0c;它是一个只读的变量。首次定义变量时…

Spring Boot - 日志功能深度解析与实践指南

文章目录 概述1. Spring Boot 日志功能概述2. 默认日志框架&#xff1a;LogbackLogback 的核心组件Logback 的配置文件 3. 日志级别及其配置配置日志级别3.1 配置文件3.2 环境变量3.3 命令行参数 4. 日志格式自定义自定义日志格式 5. 日志文件输出6. 日志归档与清理7. 自定义日…

NVIDIA DLI课程《NVIDIA NIM入门》——学习笔记

先看老师给的资料&#xff1a; NVIDIA NIM是 NVIDIA AI Enterprise 的一部分&#xff0c;是一套易于使用的预构建容器工具&#xff0c;目的是帮助企业客户在云、数据中心和工作站上安全、可靠地部署高性能的 AI 模型推理。这些预构建的容器支持从开源社区模型到 NVIDIA AI 基础…

【HF设计模式】05-单例模式

声明&#xff1a;仅为个人学习总结&#xff0c;还请批判性查看&#xff0c;如有不同观点&#xff0c;欢迎交流。 摘要 《Head First设计模式》第5章笔记&#xff1a;结合示例应用和代码&#xff0c;介绍单例模式&#xff0c;包括遇到的问题、采用的解决方案、以及达到的效果。…

【FlutterDart】页面切换 PageView PageController(9 /100)

上效果&#xff1a; 有些不能理解官方例子里的动画为什么没有效果&#xff0c;有可能是我写法不对 后续如果有动画效果修复了&#xff0c;再更新这篇&#xff0c;没有动画效果&#xff0c;总觉得感受的丝滑效果差了很多 上代码&#xff1a; import package:flutter/material.…

Electron快速入门——跨平台桌面端应用开发框架

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1f4c3;个人状态&#xff1a; 研发工程师&#xff0c;现效力于中国工业软件事业 &#x1f680;人生格言&#xff1a; 积跬步…

Android NDK开发实战之环境搭建篇(so库,Gemini ai)

文章流程 音视频安卓开发首先涉及到ffmpeg编译打包动态库&#xff0c;先了解动态库之间的cpu架构差异性。然后再搭建可运行的Android 环境。 So库适配 ⽇常开发我们经常会使⽤到第三库&#xff0c;涉及到底层的语⾳&#xff0c;视频等都需要添加so库。⽽so库的体积⼀般来说 ⾮…

【Java回顾】Day2 正则表达式----异常处理

参考资料&#xff1a;菜鸟教程 https://www.runoob.com/java/java-exceptions.html 正则表达式 有一部分没看完 介绍 字符串的模式搜索、编辑或处理文本java.util.regex包&#xff0c;包含了pattern和mathcer类&#xff0c;用于处理正则表达式的匹配操作。 捕获组 把多个字符…

Unity性能优化总结

目录 前言 移动端常见性能优化指标​编辑 包体大小优化 FPS CPU占用率 GPU占用率 内存 发热和耗电量 流量优化 前言 终于有时间了&#xff0c;我将在最近两个项目中进行优化的一些经验进行归纳总结以飨读者。因为我习惯用思维导图&#xff0c;所以归纳的内容主要以图来…

北京航空航天大学惊现技术商业“宫斗剧”!背后隐藏的内幕遭曝光!

北京航空航天大学&#xff08;以下称北航&#xff09;与源亿&#xff08;北京&#xff09;网络科技有限公司&#xff08;以下称源亿&#xff09;的派驻的员工恶意串通&#xff0c;指定北京蚂蚁非标科技有限公司&#xff08;以下称蚂蚁公司&#xff09;挖走源亿公司在现场派驻的…

transfomer深度学习实战水果识别

本文采用RT-DETR作为核心算法框架&#xff0c;结合PyQt5构建用户界面&#xff0c;使用Python3进行开发。RT-DETR以其高效的实时检测能力&#xff0c;在多个目标检测任务中展现出卓越性能。本研究针对水果数据集进行训练和优化&#xff0c;该数据集包含丰富的水果图像样本&#…

TensorFlow深度学习实战(3)——深度学习中常用激活函数详解

TensorFlow深度学习实战&#xff08;3&#xff09;——深度学习中常用激活函数详解 0. 前言1. 引入激活函数1.1 感知器1.2 多层感知器1.3 训练感知器存在的问题 2. 激活函数3. 常见激活函数3.1 sigmoid3.2 tanh3.3 ReLU3.4 ELU和Leaky ReLU 小结系列链接 0. 前言 使用激活函数…

数据结构C语言描述9(图文结合)--二叉树和特殊书的概念,二叉树“最傻瓜式创建”与前中后序的“递归”与“非递归遍历”

前言 这个专栏将会用纯C实现常用的数据结构和简单的算法&#xff1b;有C基础即可跟着学习&#xff0c;代码均可运行&#xff1b;准备考研的也可跟着写&#xff0c;个人感觉&#xff0c;如果时间充裕&#xff0c;手写一遍比看书、刷题管用很多&#xff0c;这也是本人采用纯C语言…

Leetcode打卡:设计一个ATM机器

执行结果&#xff1a;通过 题目 2241 设计一个ATM机器 一个 ATM 机器&#xff0c;存有 5 种面值的钞票&#xff1a;20 &#xff0c;50 &#xff0c;100 &#xff0c;200 和 500 美元。初始时&#xff0c;ATM 机是空的。用户可以用它存或者取任意数目的钱。 取款时&#xff0c…

vscode如何离线安装插件

在没有网络的时候,如果要安装插件,就会麻烦一些,需要通过离线安装的方式进行。下面记录如何在vscode离线安装插件。 一、下载离线插件 在一台能联网的电脑中,下载好离线插件,拷贝到无法联网的电脑上。等待安装。 vscode插件商店地址:https://marketplace.visualstudio.co…

用Tkinter制作一个用于合并PDF文件的小程序

需要安装PyPDF2库&#xff0c;具体原代码如下&#xff1a; # -*- coding: utf-8 -*- """ Created on Sun Dec 29 14:44:20 2024author: YBK """import PyPDF2 import os import tkinter as tk import windndpdf_files [] def dragged_files(f…