CSS rem 单位远不止字体大小

Avatar of Roman Rudenko
Roman Rudenko

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 $200 免费赠送额度!

以下是 Roman Rudenko 的客座文章,他是 Mobify 的疯狂科学家,他负责理解浏览器为何出现错误行为以及如何让它们正常运行。Roman 不编码的时候,会学习骑摩托车,避免撞到太多固体物体。

许多网页设计师和开发者都熟悉 CSS rem 长度 单位。但是,您可能不知道它还有几个方便的备用用途。在这篇文章中,我将介绍如何使用 CSS rem 单位来缩放特定的页面元素,而不会影响其他元素。我还将讨论如何使用它来替代支持不足的 vw(视窗宽度)单位。

对于不熟悉 rem 单位(代表“root em”)的读者来说,它提供了一种方法,可以将长度指定为根元素字体大小的几分之几。实际上,这几乎总是意味着 <html> 元素的字体大小。rem 最明显的用途是替换 em,以防止内部元素字体大小因外部元素而改变,从而避免经典的嵌套 em 缩放问题。但 rem 有效地充当自定义可调单位,因此它对于其他用途也很有用。

首先,让我们看看 rem 的工作原理以及它与 em 单位的不同之处。em 值是根据当前元素的 font-size 计算的,因此使用它来设置大小的框将随着元素或其祖先的字体大小调整而相应缩放。同时,rem 定义为根元素的 font-size。因此,所有对 rem 的引用都将返回相同的值,而不管祖先字体大小如何。实际上,rem 是一个文档范围的 CSS 变量。如今,浏览器对 rem 的支持相当不错。它在桌面上的支持有限,因为 IE8 不支持它,但常见的移动设备和平板电脑浏览器对其支持要好得多。

Rem 可以用于其典型的字体大小职责。当标记以复杂的方式嵌套时,使用 em 值的 font-size 声明可能会意外地累加,而 rem 是解决这些问题的不错选择。但是,rem 还有一些其他有趣的用途。

缩放文档元素

您可以使用 rem 来缩放文档中的某些元素,而保持其他元素不变。在这个例子中,辅助页面内容(幻灯片控件、插图名称、帖子元数据)的 font-size 是通过 rem 控制的,但主要内容保持以像素为单位的大小。

Check out this Pen!

当点击红色大小调整链接之一时,一小段 JavaScript 会调整 <html> font-size,重新调整辅助元素的大小,而不会改变主要元素的大小。

这种大小调整方式对于用户驱动的定制很有用,或者可以适应需要辅助元素更容易触摸(平板电脑)或可见(电视)的情况。如果没有 rem,则需要单独调整每个可调整元素的大小。

/* States */
html.secondary-12px { font-size: 12px; }
html.secondary-14px { font-size: 14px; }
html.secondary-16px { font-size: 16px; }

/* Primary content stays fixed */
body { font-size: 18px; }

/* Secondary content scales */
.post-inner .meta { font-size: 1rem; }
.figure .title { font-size: 1rem; }
.slideshow .controls { font-size: 1.25rem; }
.slideshow .controls a { padding: 0 0.5rem; }

替代 vw

CSS vw 单位 定义为视窗宽度的 1/100。此单位对于避免宽度计算中的累加很有用,就像 rem 避免 font-size 乘数累加一样。它也可以用于非宽度属性,例如 font-size(将文本的固定片段拟合到百分比大小的框中)或高度(保留元素的纵横比)。对 vw 的支持 仍然参差不齐。因此,我们可以改用 rem,并动态重新计算 <html> 的字体大小以匹配 vw 单位的大小。

让我们来看看此解决方法的示例实现(调整浏览器大小以查看调整)。

Check out this Pen!
body { font-size: 12px; margin: 0; padding: 0;}
.one, .two {
  border: solid #666;
  border-width: 10px; border-width: 0.01rem;   /* 1vw */
  border-radius: 30px; border-radius: 0.03rem; /* 3vw */
  font-size: 20px; font-size: 0.02rem;         /* 2vw */
  padding: 20px; padding: 0.02rem;             /* 2vw */
}

在这里,<body> 元素的 font-size 规则使内容免受 <html> font-size 更改的影响。不支持 rem 的浏览器将获得基于像素的回退;支持 rem 的浏览器将从 <html> 字体大小获取正确的大小。

此示例的早期版本允许识别 vw 属性的浏览器直接使用它,但在 Chrome 25 无法应用使用 vw 指定的边框宽度后,该版本就被删除了。基于 rem 的替代方案没有此类问题。

rem 单位可用之前,可以使用 JavaScript 直接计算和分配属性。但是,rem 使事情变得更加容易。使用 rem,只需要为整个文档进行一次分配,并且脚本不需要记住哪些属性应该以什么确切的方式进行缩放。

(function (doc, win) {
    var docEl = doc.documentElement,
        recalc = function () {
            var clientWidth = docEl.clientWidth;
            if (!clientWidth) return;

            docEl.style.fontSize = clientWidth + 'px';
            docEl.style.display = "none";
            docEl.clientWidth; // Force relayout - important to new Android devices
            docEl.style.display = "";
        };

    // Abort if browser does not support addEventListener
    if (!doc.addEventListener) return;

    // Test rem support
    var div = doc.createElement('div');
    div.setAttribute('style', 'font-size: 1rem');

    // Abort if browser does not recognize rem
    if (div.style.fontSize != "1rem") return;

    win.addEventListener('resize', recalc, false);
    doc.addEventListener('DOMContentLoaded', recalc, false);
})(document, window);

上面 JavaScript 代码中有一些需要注意的地方。首先,如果浏览器已过时并且不支持 addEventListener API 或 rem 单位本身,则该脚本将提前终止。其次,它不是将 <html> 字体大小设置为实际的 vw 单位,而是将其设置为视窗宽度(以像素为单位)。这意味着 CSS 需要将 vw 值按 100 的比例缩放,以便与 rem 一起使用,但可以避免由于 Webkit 中有限的 font-size 精度而产生的舍入错误。

其他视窗单位几乎可以用相同的方式支持。这种方法有一些缺点。例如,您不能将 rem 用于其他用途。而且,您一次只能使用一个视窗单位(vw),因此不能使用其他单位,例如 vhvminvmax

虽然现在几乎没有生产环境中的浏览器支持真正的 CSS 变量,但相当一部分浏览器允许您使用 rem 单位来做一点欺骗。如果您不需要将 rem 用于正常的字体大小,则可以使用它来运行时缩放几乎所有东西。如果您必须将 rem 用于字体大小,则可以选择使用 CSS 预处理器(Sass 或 Less)来计算正确的字体尺寸,从而使 rem 单位可用于运行时欺骗。