CSS 的变量

CSS 变量,又称为 CSS 自定义属性,是前端开发中比较新颖的知识点;但是由于很多前端开发人员专注于使用 UI 框架,CSS 反倒变成一个小众知识点了。本文就借次机会复习一下这个知识。

Overview

变量对开发的意义不言而喻:减少重复,增加可读性,并保留未来统一修改属性的便捷性。

header {
  color: #ff6f69;
}

footer {
  color: #ff6f69;
}

举个🌰,常规的 CSS 开发中,申明不同字体颜色,往往是通过硬编码 color 属性来实习。但发现没?上述代码中,headerfooter用了同一种颜色,出于防重、可读性、敏捷性等一系列软件开发中的实际考量,我们很自然地会想到使用变量:

:root {
  --red: #ff6f69;
}

header {
  color: var(--red);
}

footer {
  color: var(--red);
}

申明

CSS 变量以两个减号开头(--),并在特定选择器下定义具体的数值。下面代码中,我们给所有 div 元素定义了两个变量:--pink--green

div {
  --pink: #f7f;
  --green: #7f7;
}

CSS 还提供了一个放置全局变量的地方——:root,它的作用域就是所有 html 下的 DOM 元素。

:root {
  --bg-color: rgb(255,255,255);
  --content-padding: 1rem;
}

var()

调用变量就得用到 var 函数了,如下所示直接在 value 位置调用已定义的变量即可:

:root {
  --red: #ff6f69;
}

p {
  color: var(--red);
  border: 1px solid var(--red);
}

事实上,var 函数有两个入参:第一个参数就是 CSS 变量,而第二个参数是该变量的默认值——防止变量不存在:

p {
  border: 1px solid var(--red, #ff6f69);
}

我们还可以在申明变量时调用其他变量:

:root {
  --red: #ff6f69;
  --bg-color: var(--red);
}

甚至可以做一些简单的字符串拼接和数值计算:

:root {
  --hello: 'hello';
  --content: var(--hello)' world';
  --size: 20;
  --margin-top: calc(var(--size) * 1px);
}

注意:var 只能作用于 value,不能作用于 key。以下是不合法的:

.invalid {
  --margin: 'margin';
  var(--margin): 1rem; /* invalid */
}

局部变量

上面提到了全局变量,我们也可以声明局部变量(local variable)。所谓局部变量就是定义在 tag、class、id 等一系列选择器里的属性,这些局部变量服务于那些匹配特定选择器的元素。

举个🌰,我们为对话框定义一种特定的颜色变量——--dialog-color;如下所示,在类选择器 .dialog 里声明该变量的数值:

.dialog {
  --dialog-color: green;
}

接着对话框相关的后代选择器就能访问到该变量了:

.dialog span {
  color: var(--dialog-color);
  border: 1px solid var(--dialog-color);
}

而那些与 .dialog 无关的选择器试图调用该变量时,则不会产生效果:

.alert {
  color: var(--dialog-color); /* invalid */
}

Cascade & Inheritance

可能会有个疑问,同名的局部变量冲突了该如何抉择?

:root {
  --color: blue;
}

p {
  --color: green;
}

.alert {
  --color: red;
}

* {
  color: var(--color);
}
  • Cascade 规则

    CSS 解决冲突的方式一般就是 cascade (层叠)规则——优先级高的特征胜出。下方 html 中,三行元素的字体颜色应用的是同一个选择器——* { color: var(--color); },但最终的渲染却大相径庭。

    <span>blue</span>
    <p>green</p>
    <p class="alert">red</p>
    
    cascade rule

    原因是他们分别选用了不同的变量:

    1. 第一行的<span>选择了全局变量——blue
    2. 第二行的<p>应用的是该标签选择器里的局部变量——green
    3. 第三行虽然也是<p>标签,但是类选择器的优先级高于标签,所以类(.alert)的变量胜出——red
  • Inheritance 规则

    某些情景下,可能会出现一些违反 cascade 规则的情况,比如下方的<i>标签;它的字体应用的也是这条规则——* { color: var(--color); },表面上它会适配:root里的全局变量——blue,但最终文字被渲染成了绿色。。。

    <div class="alert">
      <p>
        <i>green</i>
      </p>
    </div>
    
    inheritance rule

    原因是还有一个叫inheritance(继承)的规则,通俗来说就是子元素的某些属性会被设置成父节点的值。这里<i>标签的父节点是<p>标签,所以它的变量就会等于p的值了——green

动态变量

在 CSS 变量出现前,less、sass 这类预编译语言已经开始使用变量了。但是 CSS 的变量又有些许不同:它是可以被 DOM 访问的,而后者只是一个编译阶段的中间变量,最终效果仅仅是文本替换——等同于 hard code。通俗点说,CSS 变量是 DOM 对象里的一个键值对(可以被更改),而 less 或 sass 的变量在 DOM 中并不会存在。

  • Responsiveness

    在实践中,我们可以根据 CSS 这个特性,动态适配特定的变量,比如我们可以在响应式编程里使用该策略:

    :root {
      --main-font-size: 16px;
    }
    
    media all and (max-width: 600px) {
      :root {
        --main-font-size: 12px;
      }
    }
    

    当屏幕变小时,--main-font-size 的值会做响应式的变化,而那些引用了该变量的 CSS 选择器也会随之更改,这一点是那些预编译语言所不能企及的。

  • Javascript

    另外,DOM 可以访问 CSS 变量就意味着我们还可以通过 JS 操作 CSS 变量。这样就可以实现修改一处变量,全局样式随之更改的效果了:

    const rootStyle = document.querySelector(":root").style;
    
    rootStyle.getPropertyValue('--color');
    rootStyle.setProperty('--color', 'green');
    rootStyle.removeProperty('--color');
    

小结

CSS 变量已得到主流浏览器的支持(IE 除外),是我们前端“武器库”里的常备工具了。相比于传统的样式表申明,CSS 变量让代码变得更加的简洁、灵活。除此之外,它是 DOM 元素的一个属性,我们也可以通过该特性实现 JS 和 CSS 的通信,是一个相对比较高级的开发技巧。

相关

文章同步发布于an-Onion 的 Github。码字不易,欢迎点赞。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容