Menu

rem、em 与 px 的区别:CSS 单位、百分比、vw 和 vh

rem 相对于根元素的字号,em 相对于当前元素的字号,px 是固定的,而 % 取决于具体属性。通过一个嵌套示例实测每种单位,看看 em 为什么会层层叠加、什么时候该用哪种单位,以及 vw、vh 和 dvh 的工作方式。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

用一个嵌套示例看懂 rem 和 em

rem 相对于根元素的字号(即 <html> 元素,默认 16px)。em 相对于当前字号,对 font-size 来说就是父元素的字号。把同一个类嵌套三层,区别就显现出来了:

打印出来的尺寸是浏览器计算得到的结果。每个 1.25em 都会乘以父元素的字号:20px、25px、31.25px。每个 1.25rem 只看根元素:20px、20px、20px。正是这种层层叠加,让 em 字号在嵌套的列表和菜单中失控,这也是大多数网站用 rem 设置字号的原因。

每种单位相对于什么

单位相对于默认时 1 个单位等于
px不相对于任何东西(一个 CSS 像素)1px
rem根元素(html)的字号16px
em元素的字号;对 font-size 本身来说是父元素的字号顶层为 16px
%取决于属性(见下文)
vw / vh视口宽度 / 高度的 1%
dvh、svh、lvh动态、小或大视口高度的 1%
vmin / vmax视口较短 / 较长边的 1%
ch当前字体中“0”的宽度大约半个 em
ex当前字体的 x 字高大约半个 em

CSS 像素不是设备像素:在 3 倍屏的手机上,1px 有 3 个设备像素宽。它在不同屏幕上的视觉大小大致相同,所以边框和阴影用 px 仍然没问题。

用 em 设置随文字缩放的间距

em 不只用于字号。用在 padding、margin、width 或 border-radius 上时,它的意思是“这个元素字号的多少倍”。这样一个按钮类就能适用于任何尺寸:

每个按钮的垂直内边距是字号的一半,水平内边距等于一个字号,所以三个按钮看起来就是同一个按钮的三种尺寸。如果用 px 设置内边距,每种尺寸都得单独写一条内边距规则。

注意,按钮默认不会继承页面的字号(浏览器给表单控件设置了自己的字号),所以这里中间的按钮在 Chrome 中是 13.33px,而不是 16px。想让按钮和输入框与页面一致时,给它们加上 font: inherit。

百分比:取决于属性

% 没有统一的含义。每个属性各自规定它是谁的百分比:

属性% 相对于
width、max-width、left、right父元素(包含块)的宽度
height、top、bottom父元素的高度,并且只有父元素设置了高度时才有效
padding、margin(四个方向)父元素的宽度
font-size父元素的字号(150% = 1.5em)
line-height元素自身的字号
transform: translate()元素自身的尺寸

width: 50% 是 200px,即父元素的一半。padding-top: 5% 是 20px:父元素宽度的 5%,而不是高度的 5%。而 height: 50% 不起作用,因为父元素没有设置高度,所以子元素的高度只等于它的文字加上那段内边距。宽度和高度页面详细讲解了这条规则。

vw、vh 与手机地址栏

1vw 是视口宽度的 1%,1vh 是视口高度的 1%。它们适合用来做全屏区块和流式尺寸(常放在 clamp() 里)。在手机上,vh 有个坑:滚动时浏览器的地址栏会滑入滑出,而 100vh 是不含地址栏时的高度,所以地址栏显示时,height: 100vh 的首屏区块底部会被截掉。较新的单位解决了这个问题:

单位使用的高度
100vh在大多数移动浏览器上是大视口(工具栏隐藏)
100svh小视口(工具栏显示):始终放得下
100lvh明确指定大视口
100dvh动态视口:随工具栏移动而变化
.hero {
  min-height: 100vh;  /* fallback for older browsers */
  min-height: 100svh; /* fits the screen even with the address bar visible */
}

vw 也有自己的坑:当垂直滚动条占据空间时(比如在 Windows 上),100vw 包含滚动条的宽度,所以 width: 100vw 可能比页面更宽,从而出现水平滚动条。要表示“父元素的完整宽度”,请用 width: 100%。

用 ch 控制易读的行长

ch 是字符“0”的宽度,所以 max-width: 65ch 能把段落限制在每行大约 65 个字符,无论字号多大,都是舒适的阅读长度:

该用哪种单位

用途使用原因
字号rem处处一致,尊重读者的字号设置
组件中的内边距和间隙rem 或 em需要随组件自身文字变大时用 em
边框、细线、阴影px在任何字号下都应保持纤细
布局宽度%、fr,以及用 rem 或 ch 设置的 max-width跟随容器,限制行长
全屏区块svh / dvh(加上 vh 作为后备)应对手机地址栏
流式字号混合 rem 和 vw 的 clamp()在限定范围内随屏幕变大

为什么 rem 尊重读者而 px 不尊重

浏览器有一个默认字号设置,需要更大文字的人会用到它。它会改变根元素的字号,所以所有用 rem 和 em 设置的内容都会随之变大。而 font-size: 14px 无论读者选了什么都保持 14px。浏览器缩放也会缩放 px,但很多读者依赖的是字号设置。这就是用 rem 设置字号的主要原因。

有些网站会设置 html { font-size: 62.5%; },让 1rem 等于 10px,方便计算。它仍然尊重读者的设置(读者所选字号的 62.5%),但记得在 body 上设置一个易读的 font-size,否则所有没有单独设置字号的元素都会显示 10px 的文字。

常见错误

  • 在嵌套元素中用 em 设置字号。它会在每一层相乘。字号请用 rem。
  • height: 50% 不起作用。父元素需要设置高度,百分比高度才会生效。
  • 以为 padding-top: 10% 跟随父元素的高度。垂直方向的内边距和外边距百分比使用的是父元素的宽度。
  • width: 100vw 导致出现水平滚动条。它包含了滚动条的宽度。改用 100%。
  • height: 100vh 在手机上被截断。改用 100svh 或 100dvh。
  • 设置 html { font-size: 12px }。根元素上的 px 值会覆盖读者对整个页面的字号设置。不要改根元素字号,或者使用百分比。

常见问题

rem 和 em 有什么区别?

rem 相对于根元素(<html>)的字号,所以 1.5rem 在页面上任何地方都一样大。em 相对于元素自身的字号(对 font-size 来说是父元素的字号),所以元素嵌套时它会层层叠加。

1rem 等于多少像素?

默认是 16px,因为浏览器把根元素字号设为 16px。如果用户在浏览器设置里调大了默认字号,或者你设置了 html { font-size: ... },1rem 也会随之改变。

字号应该用 rem 还是 px?

字号用 rem。它会尊重读者在浏览器中设置的默认字号,而 px 会忽略这个设置。浏览器缩放对两者都有效,但不是每个读者都会使用缩放。

什么时候应该用 em 而不是 rem?

当间距需要随元素自身的文字缩放时用 em,比如按钮的内边距,或文字旁边图标的尺寸。这样同一个类在任何字号下都显示得恰到好处。字号和页面级间距用 rem,这样就不会层层叠加。

100vh 和 100dvh 有什么区别?

在移动浏览器上,100vh 是地址栏隐藏时的高度,所以地址栏显示时,100vh 的区块比可见屏幕更高。100dvh(动态视口高度)会随地址栏的出现和消失而变化,而 100svh 始终是较小的那个高度。

Coddy 编程语言插图

和 Coddy 一起学编程

开始