【CSS in Depth 2 精译】2.2 em 和 rem + 2.2.1 使用 em 定义字号
当前内容所在位置
- 第一章 层叠、优先级与继承
- 第二章 相对单位
- 2.1 相对单位的威力
- 2.1.1 响应式设计的兴起
- 2.2 em 与 rem ✔️
- 2.2.1 使用 em 定义字号 ✔️
- 2.2.2 使用 rem 设置字号
- 2.3 告别像素思维
- 2.4 视口的相对单位
- 2.5 无单位的数值与行高
- 2.6 自定义属性
- 2.7 本章小结
2.2 em 与 rem
em 是最常见的相对长度单位,也是排版中用于设置字体大小(即字号)的度量单位。在 CSS 中,1em 表示当前元素的字号,具体大小取决于作用的元素。图 2.1 为一个内边距 padding 为 1em 的 div 元素:

图 2.1 1em 内边距等同于当前字号(虚线用于展示内边距大小)
其样式代码如代码清单 2.1 所示。规则集指定字号为 16px,即该元素在本地定义的 1em 的实际大小。随后又用 em 设置了其内边距。将代码清单 2.1 中的样式拷到一个新样式表,并在元素 <div class="padded" 中放一些文字,看看最终效果。
代码清单 2.1 对 padding 使用相对单位 em
.padded {font-size: 16px;padding: 1em; /* Sets padding on all sides equal to font-size */
}
可以看到 padding 的值为 1em,再乘以字号,则渲染出了大小为 16px 的内边距。重点来了:浏览器根据相对单位声明的值计算出的绝对单位值,称为 计算值(computed value)
若将本例中的 padding 改为 1.5em,则计算值变为 24px;如果另一个选择器也指向该元素并设置了不同的字号,则会修改本地 em 的大小,算出的 padding 值也会随之更新。
使用 em 来设置 padding、height、width 及 border-radius 会非常方便,因为当元素继承了不同的字号,或者用户变更了字体设置,这些样式也会跟随当前元素均匀地缩放。
图 2.2 展示了两个不同大小的盒子,它们的字号、内边距及圆角半径都各不相同:

图 2.2 设置了相对大小的内边距和圆角半径会随字号的改变而同步缩放
为上述盒子定义样式时,可以用 em 指定其内边距及圆角半径,比如设为 1em;此时修改各元素的字号,这些属性就会随着字体一起缩放。
按以下代码更新 HTML 示例文件,并给元素分别添加样式类 box-small、box-large,作为尺寸修饰符:
<span class="box box-small">Small</span>
<span class="box box-large">Large</span>
接着,按照代码清单 2.2 所示添加样式。该样式以 em 为单位定义了一个 box 类,同时还指定了上述一大一小两个修饰符,利用不同的字号大小缩放各自所在的元素。
代码清单 2.2 将 em 应用到不同元素
.box {padding: 1em;border-radius: 1em;background-color: lightgray;
}.box-small {font-size: 12px; /* 与 L12 不同的字号,该字号定义了当前元素的 em 大小 */
}.box-large {font-size: 18px; /* 与 L8 不同的字号,该字号定义了当前元素的 em 大小 */
}
这就是 em 的强大之处:定义某个元素的大小后,只需一句变更字号的样式声明,就能缩放整个元素。稍后会再举一例,在此之前,先聊聊 em 和字号设置相关的话题。
2.2.1 使用 em 定义字号
当 em 作用于 font-size 属性(property)时,其表现略有不同。之前讲到,当前元素字号决定了 em 的大小;但如果声明 font-size: 1.2em 会怎样呢?一个字号肯定不能等于自身的 1.2 倍;事实上,font-size 属性上的 em 的大小是基于它继承的字号计算出来的。
举个简单的例子。如图 2.3 所示,有两段字号各异的文字,样式按代码清单 2.3 所示,用 em 进行设置。

图 2.3 使用 em 定义两种不同的字号
按以下代码更新页面。第一行文本在 <body> 标签内,则会按 body 的字号进行渲染;带 slogan 样式类那行,则会继承该字号来渲染:
<body>We love coffee<p class="slogan">We love coffee</p> <!-- slogan 继承了 <body> 的字号 -->
</body>
代码清单 2.3 中的样式指定了 body 的字号,为便于演示,这里用像素作单位。接着使用 em 来增大 slogan 的字体大小。
代码清单 2.3 使用 em 定义 font-size
body {font-size: 16px;
}
.slogan {font-size: 1.2em; /* 经计算,字号为该元素继承字号的 1.2 倍 */
}
此时 slogan 的字号为 1.2em。要拿到计算出的像素值,需要参考继承来的大小为 16px 的字体。由于 16 × 1.2 = 19.2,所以算出的实际字体大小为 19.2px。
提示
如果已知字号的像素值,但是想声明为 em 的形式,则用这个简单公式换算:目标像素值 ÷ 父元素(继承)字号的像素值。比如,目标字号为
10px,该元素继承的字号为12px,转成em则为 10 / 12 = 0.8333em;目标字号16px、父级字号12px,转成em则为 16 / 12 = 1.3333em。本章还会进行几次类似的计算。
大多数浏览器的默认字号均为 16px,记住这些知识将大有好处。用专业的话来讲,关键字 medium 的值经计算为 16px 大小。
1 em 同时用于字号和其他属性
至此,我们已经用 em 定义了字号(基于继承的字号),并且通过 em 定义了其他属性,比如 padding 和 border-radius(基于当前元素的字号)。当使用 em 给同一个元素同时设置字号和其他属性时,情况就变得复杂多了。此时,浏览器必须先计算字号,然后再利用算出的结果进一步算出其余属性的具体取值。这两类属性的声明值可能相同,但计算值却未必相等。
前面的示例中,带有 slogan 样式类的元素最终字号为 19.2px(=继承字号 16px × 1.2em)。如图 2.4 所示,元素还是 slogan 不变,只是内边距 padding 调大到 1.2em。背景设为灰色以便观察内边距的实际效果。可以看到内边距比字号还要偏大一些,尽管二者的声明值都相同。

图 2.4 em 定义的字号有别于同样用 em 定义的内边距
这是因为段落标签从 body 元素继承了 16px 的字号,实际字号变为了 19.2px。此时 19.2px 即为该段落元素 1em 的最终大小,padding 的具体大小也是基于这个值进行计算的。相应的 CSS 代码如下所示,更新到示例页查看最终效果:
代码清单 2.4 使用 em 定义 font-size 和 padding
body {font-size: 16px;
}
.slogan {font-size: 1.2em; /* 计算值为 19.2px */padding: 1.2em; /* 计算值为 23.04px */background-color: #ccc;
}
本例中,padding 的声明值为 1.2em,乘以 19.2px(当前元素字号),得到计算值 23.04px。尽管 font-size 和 padding 的声明值相同,但计算值却不相等。
2 字体缩小的问题
当 em 用于设置具有多级嵌套结构的元素字体时,也会产生意想不到的结果。为了算出每个元素的具体大小,就得知道它们继承的字号是多少;如果父元素碰巧也是用 em 来定义的,就要看该父元素的继承值是多少,以此类推,一直沿着 DOM 树向上考察。
当使用 em 给列表元素定义字号、列表又嵌套了多级子列表时,问题很快就显现出来了。几乎每一位 Web 开发人员在职业生涯的某个阶段加载这样的页面都会碰到类似图 2.5 所示的情况。文字在逐级缩小!正是这样的问题让广大开发人员对 em 敬而远之。

图 2.5 字号设为 0.8em 导致嵌套列表中的文字逐级缩小
当列表又嵌套了多级子列表、并且给列表逐级设置基于 em 的相对字号时,就会发生文字缩小的现象(译注:其值须小于 1 才会缩小,大于 1 则为逐级放大)。如代码清单 2.5 所示,将无序列表的字号设为 0.8em 后,由于该选择器会对页面上每一个 <ul> 元素生效,从而让内层元素逐级继承外层元素的 em 字号,字体的坍缩幅度也随之逐级叠加。
代码清单 2.5 使用 em 指定无序列表的字号
body {font-size: 16px;
}
ul {font-size: 0.8em;
}
若将上述样式应用到代码清单 2.6 所示的 HTML 中,就会出问题。每一个 <ul> 都从父列表继承字号,这些 em 值只会让字体逐渐缩小:
代码清单 2.6 嵌套列表 HTML
<ul><li>Top level<ul> <!-- 该列表嵌套在第一个列表中,继承第一个列表的字号 --><li>Second level<ul> <!-- 该列表嵌套在上一个列表中,继承第二个列表的字号 --><li>Third level<ul> <!-- 依此类推 --><li>Fourth level<ul><li>Fifth level</li></ul></li></ul></li></ul></li></ul></li>
</ul>
每级列表的字号都是其父列表的 0.8 倍,即:第一个列表的实际字号为 12.8px,下一级则为 10.24px(12.8px × 0.8),第三级则为 8.192px,依此类推。同理,如果各级字号大于 1em,实际字体大小会逐渐增大。而我们想要的效果是只设置最外层的字号,然后让里面的字体大小保持一致,如图 2.6 所示。

图 2.6 文字大小正常的嵌套列表
实现上述效果的一种解决方案如代码清单 2.7 所示。先设置一级列表的字号为 0.8em(同代码清单 2.5);再用第二个选择器选中除最顶层外、所有无序列表下的所有后代列表,并设置字号等于其父级字号,最后得到如图 2.6 所示的效果。
代码清单 2.7 更正文字缩小问题
ul {font-size: 0.8em;
}
ul ul { font-size: 1em; /* 嵌套列表的字号应与其父级字号一致 */
}
问题倒是解决了,尽管不是很理想——定好一个字号,立马又用另一个字号去覆盖掉。如果不用提高选择器的优先级来覆盖规则,就再好不过了。
至此,各位也该心里有数了:使用 em 稍有不慎就会变得难以驾驭。em 用在内边距、外边距以及元素尺寸上时挺省心的;可一旦用到字号上,省心就容易变成闹心。好在 CSS 还有一个更好的方案—— rem。
相关文章:
【CSS in Depth 2 精译】2.2 em 和 rem + 2.2.1 使用 em 定义字号
当前内容所在位置 第一章 层叠、优先级与继承第二章 相对单位 2.1 相对单位的威力 2.1.1 响应式设计的兴起 2.2 em 与 rem ✔️ 2.2.1 使用 em 定义字号 ✔️2.2.2 使用 rem 设置字号 2.3 告别像素思维2.4 视口的相对单位2.5 无单位的数值与行高2.6 自定义属性2.7 本章小结 2.…...
C++Primer Plus 第十四章代码重用:14.4.4 数组模板示例和非类型参数
系列文章目录 14.4.4 数组模板示例和非类型参数 提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 系列文章目录14.4.4 数组模板示例和非类型参数 14.4.4 数组模板示例和非类型参数 模板常用作容器类,这…...
短视频哪个软件好用?成都柏煜文化传媒有限公司
短视频哪个软件好用?一文带你了解各大平台特色 随着移动互联网的飞速发展,短视频已经成为现代人生活中不可或缺的一部分。市面上涌现出众多短视频平台,它们各具特色,满足了不同用户的需求。那么,短视频哪个软件好用呢…...
金融科技:重塑用户体验,驱动满意度飙升
随着科技的飞速发展,金融科技(FinTech)已经深入到我们生活的每一个角落,从日常支付到投资理财,再到跨境汇款,它都在悄无声息地改变着我们的金融行为。而在这背后一个不可忽视的驱动力就是金融科技对用户体验…...
JavaScript——算术运算符
目录 任务描述 相关知识 - * / %运算符 递增运算符和递减运算符 编程要求 任务描述 本关任务:给定两个字符串变量,把它们转为数字后相除,拼接被除数、除数和余数为一个新的字符串。 例如:a 为 "5",b 为…...
备份SQL Server数据库并还原到另一台服务器
我可以将SQL Server数据库备份到另一台服务器吗? 有时您可能希望将 SQL数据库从一台服务器复制到另一台服务器,或者将计算机复制到计算机。可能的场景包括测试、检查一致性、从崩溃的机器恢复数据库、在不同的机器上处理同一个项目等。 是的,…...
二刷算法训练营Day45 | 动态规划(7/17)
目录 详细布置: 1. 139. 单词拆分 2. 多重背包理论基础 3. 背包总结 3.1 背包递推公式 3.2 遍历顺序 01背包 完全背包 详细布置: 1. 139. 单词拆分 给你一个字符串 s 和一个字符串列表 wordDict 作为字典。如果可以利用字典中出现的一个或多个单…...
大模型项目落地时,该如何估算模型所需GPU算力资源
近期公司有大模型项目落地。在前期沟通时,对于算力估算和采购方案许多小伙伴不太了解,在此对相关的算力估算和选择进行一些总结。 不喜欢过程的可以直接 跳到HF上提供的模型计算器 要估算大模型的所需的显卡算力,首先要了解大模型的参数基础知识。 大模型的规模、参数的理解…...
LLM应用开发-RAG系统评估与优化
前言 Hello,大家好,我是GISer Liu😁,一名热爱AI技术的GIS开发者,在上一篇文章中,我们学习了如何基于LangChain构建RAG应用,并且通过Streamlit将这个RAG应用部署到了阿里云服务器;&am…...
秋招突击——第七弹——Redis快速入门
文章目录 引言Redis是什么 正文对象String字符串面试重点 List面试考点 压缩列表ZipList面试题 Set面试题讲解 Hash面试重点 HASHTABLE底层面试考点 跳表面试重点 ZSET有序链表面试重点 总结 引言 在项目和redis之间,我犹豫了一下,觉得还是了解学习一下…...
软考初级网络管理员__操作系统单选题
1.在Windows资源管理器中,假设已经选定文件,以下关于“复制”操作的叙述中,正确的有()。 按住Ctr键,拖至不同驱动器的图标上 按住AIt键,拖至不同驱动器的图标上 直接拖至不同驱动器的图标上 按住Shift键࿰…...
从入门到精通:网络编程套接字(万字详解,小白友好,建议收藏)
一、预备知识 1.1 理解源IP地址和目的IP地址 在网络编程中,IP地址(Internet Protocol Address)是每个连接到互联网的设备的唯一标识符。IP地址可以分为IPv4和IPv6两种类型。IPv4地址是由32位二进制数表示,通常分为四个八位组&am…...
dledger原理源码分析系列(一)架构,核心组件和rpc组件
简介 dledger是openmessaging的一个组件, raft算法实现,用于分布式日志,本系列分析dledger如何实现raft概念,以及dledger在rocketmq的应用 本系列使用dledger v0.40 本文分析dledger的架构,核心组件;rpc组…...
第七节:如何浅显易懂地理解Spring Boot中的依赖注入(自学Spring boot 3.x的第二天)
大家好,我是网创有方,今天我开始学习spring boot的第一天,一口气写了这么多。 这节通过一个非常浅显易懂的列子来讲解依赖注入。 在Spring Boot 3.x中,依赖注入(Dependency Injection, DI)是一个核心概念…...
Postman自动化测试实战:使用脚本提升测试效率
在软件开发过程中,接口测试是确保后端服务稳定性和可靠性的关键步骤。Postman作为一个流行的API开发工具,提供了强大的脚本功能来实现自动化测试。通过在Postman中使用脚本,测试人员可以编写测试逻辑,实现测试用例的自动化执行&am…...
CSMA/CA并不是“公平”的
CSMA/CA会造成过于公平,对于最需要流量的节点,是最不友好的,而对于最不需要流量的节点,则是最友好的。 CSMA/CA是优先公平来工作的。 CSMA/CA首先各节点使用DIFS界定air idle,在此期间大家都等待 其次,为了同时发送引起碰撞,在DIFS之后随机从CWmin和CWmax之间选择一个时…...
【漏洞复现】I doc view——任意文件读取
声明:本文档或演示材料仅供教育和教学目的使用,任何个人或组织使用本文档中的信息进行非法活动,均与本文档的作者或发布者无关。 文章目录 漏洞描述漏洞复现测试工具 漏洞描述 I doc view 在线文档预览是一个用于查看、编辑、管理文档的工具…...
图数据库 vs 向量数据库
最近大模型出来之后,向量数据库重新翻红,业界和市场上有不少声音认为向量数据库会极大的影响图数据库,图数据库市场会萎缩甚至消失,今天就从技术原理角度来讨论下图数据库和向量数据库到底差别在哪里,适合什么场景&…...
企业品牌出海第一站 维基百科词条创建
维基百科是一部内容开放、自由的网络百科全书,旨在创造一个涵盖所有领域知识,服务所有互联网用户的知识性百科全书。其在国外应用非常广泛且认可度很高,国内品牌出海或国际品牌都很有必要创建企业自己的维基百科页面,以及企业高管的个人维基百科页面。 如…...
Windows下activemq集群配置(broker-network)
1.activemq版本信息 activemq:apache-activemq-5.18.4 2.activemq架构 3.activemq集群配置 activemq集群配置基于Networks of Brokers 这种HA方案的优点:是占用的节点数更少(只需要2个节点),而且2个broker都可以响应消息的接收与发送。不足ÿ…...
C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...
Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...
ip子接口配置及删除
配置永久生效的子接口,2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
uniapp手机号一键登录保姆级教程(包含前端和后端)
目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号(第三种)后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...
