[译] 关于 CSS 变量,你须要了解的一切

本文是我新写的电子书的第一章(电子书目前已支持 pdf 和 mobi 格式下载)。css

大多数编程语言都支持变量。然而遗憾的是,CSS 从一开始就缺少对原生变量的支持。html

你写 CSS 吗?若是写的话你就知道是无法使用变量的。固然了,除非你使用像 Sass 这样的预处理器。前端

像 Sass 这样的预处理器是把变量的使用做为一大亮点。这是一个很是好的理由去尝试使用这类预处理器。固然了,这个理由已然足够好了。android

Web 技术发展是很是快的,在此我很高兴地报告 如今 CSS 支持变量了ios

然而预处理器还支持更多优秀特性,CSS 变量仅仅是其中之一。这些特性使得 Web 技术更加贴近将来。git

这篇指南将向你展现变量是如何在原生 CSS 中工做的,以及怎样使用变量让你的编程工做更轻松。github

你将学到

首先我将带你粗略过一遍 CSS 变量的基础知识。我相信任何理解 CSS 变量的尝试都必须从这里开始。npm

学习基础知识是一件很是酷的事。更酷的是使用基础知识来构建一个真正的应用。编程

所以,我将构建三个可以体现 CSS 变量的使用及其易用性的项目,用这种方式把两件事结合起来。下面是对这三个项目的快速预览。后端

项目 1: 使用 CSS 变量建立一个有变化效果的组件

你可能已经构建过一个有变化效果的组件了。不管你是使用 React,Angular 仍是 Vue,使用 CSS 变量都会让构建过程更简单。

使用 CSS 变量建立一个有变化效果的组件。

能够在 Codepen 上查看这个项目。

项目 2: 使用 CSS 变量实现主题定制

可能你已经看过这个项目了。我会向你展现使用 CSS 变量来定制全站主题有多么容易。

使用 CSS 变量定制全站主题。

能够在 Codepen 上查看这个项目。

项目 3: 构建 CSS 变量展位

这是最后一个项目了,不要在乎这个项目名,我想不出更好的名字了。

盒子的颜色是动态更新的。

请注意盒子的颜色是如何动态更新的,以及盒子容器是如何随着输入范围值的变化进行 3D 旋转的。

.

这个项目展现了使用 JavaScript 更新 CSS 变量的便利性,从中你还会尝到响应式编程的甜头。

这会是很是好玩的!

花点时间在 Codepen 上玩一玩。

注意:本文假定你对 CSS 已得心应手。若是你对 CSS 掌握地不是很好,或者想学习如何创做出惊艳的 UI 效果,我建议你去学习个人 CSS 进阶课程(共 85 课时的付费课程)。本文内容是该课程的一个节选。😉

为什么变量如此重要

若是你对预处理器和原生 CSS 中的变量并不熟悉的话,如下几个缘由能够为你解答为什么变量如此重要。

缘由 #1:使得代码更可读

无需多言,你就能够判断出,变量使得代码可读性更好,更易于维护。

缘由 #2:易于在大型文档中进行修改

若是把全部的常量都维护在一个单独文件中,想改动某一变量时就无需在上千行代码间来回跳转进行修改。

这变得很是容易,仅仅在一个地方进行修改,就搞定了。

缘由 #3:定位打字错误更快

在多行代码中定位错误很是痛苦,更痛苦的是错误是由打字错误形成的,它们很是难定位。善于使用变量能够免除这些麻烦。

至此,可读性和可维护性是主要优势。

感谢 CSS 变量,如今咱们在原生 CSS 中也能享受到以上这些优势了。

定义 CSS 变量

先以你已经很熟悉的东西开始:JavaScript 中的变量。

JavaScript 中,一个简单的变量声明会像这样:

var amAwesome;
复制代码

而后你像这样能够赋值给它:

amAwesome = "awesome string"
复制代码

在 CSS 中,以两个横线开头的“属性”都是 CSS 变量。

/*你能够找到变量吗? */
.block {
 color: #8cacea;
--color: blue
}
复制代码

CSS 变量也被称为“自定义属性”。

CSS 变量做用域

还有一点须要注意。

请记住 JavaScript 中变量是有做用域的,要么是全局做用域,要么就是局部做用域

CSS 变量也是如此。

思考一下下面这个例子:

:root {
  --main-color: red
}
复制代码

:root 选择器容许你定位到 DOM 中的最顶级元素或文档树。

因此,这种方式声明的变量就属于具备全局做用域的变量。

明白了吗?

局部变量与全局变量。

示例 1

假设你想建立一个 CSS 变量来存储站点的主题颜色。

你会怎么作呢?

  1. 建立一个做用域选择器。经过 :root 建立一个全局变量。
:root {

}
复制代码
  1. 定义变量
:root {
 --primary-color: red
}
复制代码

请记住,在 CSS 中,以两个横线开头的“属性”都是 CSS 变量,好比 --color

就是这么简单。

使用 CSS 变量

变量一旦被定义并赋值,你就能够在属性值内使用它了。

可是有个小问题。

若是你用过预处理器的话,必定已经习惯经过引用变量名来使用该变量了。好比:

$font-size: 20px

.test {
  font-size: $font-size
}
复制代码

原生 CSS 变量有些不一样,你须要经过 var() 函数来引用变量。

在上面这个例子中,使用 CSS 变量就应该改为这样:

:root {
  --font-size: 20px
}

.test {
  font-size: var(--font-size)
}
复制代码

两种写法大不同。

请记得使用 var 函数。

一旦你习惯了这种方式,就会爱上 CSS 变量的。

另外一个重要的注意事项是,在 Sass 这类预处理器中,你能够在任意地方使用变量,作各类计算,可是须要注意,在原生 CSS 中,你只能将变量设置为属性值。

/*这是错的*/
.margin {
--side: margin-top;
var(--side): 20px;
}
复制代码

因为属性名非法,这段声明会抛出语法错误

CSS 变量也不能作数学计算。若是须要的话,能够经过 CSS 的 calc() 函数进行计算。接下来咱们会经过示例来阐述。

/*这是错的*/
.margin {
--space: 20px * 2;
font-size:  var(--space);  // 并不是 40px
}
复制代码

若是你必需要作数学计算的话,能够像这样使用 calc() 函数:

.margin {
--space: calc(20px * 2);
font-size:  var(--space);  /*等于 40px*/
}
复制代码

关于属性的一些事

如下是几个须要阐述的属性行为:

1. 自定义属性就是普通属性,能够在任意元素上声明自定义属性

在 p,section,aside,root 元素,甚至伪元素上声明自定义属性,均可以运行良好。

这些自定义属性工做时与普通属性无异。

2. CSS 变量由普通的继承与级联规则解析

请思考如下代码:

div {
  --color: red;
}

div.test {
  color: var(--color)
}

div.ew {
  color: var(--color)
}
复制代码

像普通变量同样,--color 的值会被 div 元素们继承。

3. CSS 变量能够经过 @media 和其它条件规则变成条件式变量

和其它属性同样,你能够经过 @media 代码块或者其它条件规则改变 CSS 变量的值。

举个例子,如下代码会在大屏设备下改变变量 gutter 的值。

:root {
 --gutter: 10px
}

@media screen and (min-width: 768px) {
    --gutter: 30px
}
复制代码

对于响应式设计颇有用。

4. HTML 的 style 属性中可使用 CSS 变量。

你能够在行内样式中设置变量值,变量依然会如期运行。

<!--HTML-->
<html style="--color: red">

<!--CSS-->
body {
  color: var(--color)
}
复制代码

行内设置变量值。

要注意这一点,CSS 变量是区分大小写的。我为了减少压力,选择都采用小写形式,这件事见仁见智。

/*这是两个不一样的变量*/
:root {
 --color: blue;
--COLOR: red;
}
复制代码

解析多重声明

与其它属性相同,多重声明会按照标准的级联规则解析。

举个例子:

/*定义变量*/
:root { --color: blue; }
div { --color: green; }
#alert { --color: red; }

/*使用变量*/
* { color: var(--color); }
复制代码

根据以上的变量声明,下列元素是什么颜色?

<p>What's my color?</p> <div>and me?</div> <div id='alert'> What's my color too?
  <p>color?</p>
</div>
复制代码

你想出答案了吗?

第一个 p 元素颜色是 蓝色p 选择器上并无直接的颜色定义,因此它从 :root 上继承属性值

:root { --color: blue; }
复制代码

第一个 div 元素颜色是 绿色。这个很简单,由于有变量直接定义在 div 元素上

div { --color: green; }
复制代码

具备 ID 为 alertdiv 元素颜色不是绿色,而是 红色

#alert { --color: red; }
复制代码

因为有变量做用域直接是在这个 ID 上,变量所定义的值会覆盖掉其它值。#alert 选择器是一个更为特定的选择器。

最后,#alert 元素内的 p 元素颜色是 红色

这个 p 元素上并无变量声明。因为 :root 声明的颜色属性是 蓝色,你可能会觉得这个 p 元素的颜色也是 蓝色

:root { --color: blue; }
复制代码

如其它属性同样, CSS 变量是会继承的,所以 p 元素的颜色值继承自它的父元素 #alert

#alert { --color: red; }
复制代码

小测验的答案。

解决循环依赖

循环依赖会出如今如下几个场景中:

  1. 当一个变量依赖本身自己时,也就是说这个变量经过 var() 函数指向本身时。
:root {
  --m: var(--m)
}

body {
  margin: var(--m)
}
复制代码
  1. 两个以上的变量互相引用。
:root {
  --one: calc(var(--two) + 10px);
  --two: calc(var(--one) - 10px);
}
复制代码

请注意不要在你的代码中引入循环依赖。

使用非法变量会怎样?

语法错误机制已被废弃,非法的 var() 会被默认替换成属性的初始值或继承的值。

思考一下下面这个例子:

:root { --color: 20px; }
p { background-color: red; }
p { background-color: var(--color); }
复制代码

正如咱们所料,--color 变量会在 var() 中被替换,可是替换后,属性值 background-color: 20px 是非法的。因为 background-color 不是可继承的属性,属性值将默认被替换成它的初始值即 transparent

注意,若是你没有经过变量替换,而是直接写 background-color: 20px 的话,这个背景属性声明就是非法的,则使用以前的声明定义。

当你本身写声明时,状况就不同了。

使用单独符号时要当心

当你用下面这种方式来设置属性值时,20px 则会按照单独符号来解析。

font-size: 20px
复制代码

有一个简单的方法去理解,20px 这个值能够看做是一个单独的 “实体”。

在使用 CSS 变量构建单独符号时须要很是当心。

举个例子,思考如下代码:

:root {
 --size: 20
}

div {
  font-size: var(--size)px /*这是错的*/
}
复制代码

可能你会觉得 font-size 的值是 20px,那你就错了。

浏览器的解释结果是 20 px

请注意 20 后面的空格

所以,若是你必须建立单独符号的话,请用变量来表明整个符号。好比 --size: 20px,或者使用 calc 函数好比 calc(var(--size) * 1px) 中的 --size 就是等于 20

若是你没看懂的话也不用担忧,在下个示例中我会解释地更详细。

一颗赛艇!

如今咱们已经到了期待已久的章节了。

我将经过构建几个有用的小项目,在实际应用中引导你了解以前所学的理论。

让咱们开始吧。

项目 1: 使用 CSS 变量建立一个有变化效果的组件

思考一下须要构建两个不一样按钮的场景,两个按钮的基本样式相同,只有些许不一样。

这个场景中,按钮的 background-colorborder-color 属性不一样。

那么你会怎么作呢?

这里有一个典型解决方案。

建立一个叫 .btn 的基础类,而后加上用于变化的类。举个例子:

<button class="btn">Hello</button>
<button class="btn red">Hello</button>
复制代码

.btn 包括了按钮上的基础样式,如:

.btn {
  padding: 2rem 4rem;
  border: 2px solid black;
  background: transparent;
  font-size: 0.6em;
  border-radius: 2px;
}

/*hover 状态下*/
.btn:hover {
  cursor: pointer;
  background: black;
  color: white;
}
复制代码

在哪里引入变化量呢?

这里:

/* 变化 */

.btn.red {
  border-color: red
}
.btn.red:hover {
  background: red
}
复制代码

你看到咱们将代码复制到好几处么?这还不错,可是咱们能够用 CSS 变量来作的更好。

第一步是什么?

用 CSS 变量替代变化的颜色,别忘了给变量加上默认值。

.btn {
   padding: 2rem 4rem;
   border: 2px solid var(--color, black);
   background: transparent;
   font-size: 0.6em;
   border-radius: 2px;
 }

 /*hover 状态下*/
 .btn:hover {
  cursor: pointer;
   background: var(--color, black);
   color: white;
 }
复制代码

当你写下 background: **var(--color, black)** 时,就是将背景色的值设置为变量 --color 的值,若是变量不存在的话则使用默认值 **black**

这就是设置变量默认值的方法,与在 JavaScript 和其它语言中的作法同样。

这是使用变量的好处。

使用了变化量,就能够用下面这种方法来应用变量的新值:

.btn.red {
   --color: red
 }
复制代码

就是这么简单。如今当使用 .red 类时,浏览器注意到不一样的 --color 变量值,就会当即更新按钮的样式了。

若是你要花不少时间来构建可复用组件的话,使用 CSS 变量是一个很是好的选择。

这是并排比较:

不用 CSS 变量 VS 使用 CSS 变量。

若是你有很是多的可变选项的话,使用 CSS 变量还会为你节省不少打字时间。

看出不一样了吗??

项目 2: 使用 CSS 变量实现主题定制

我很肯定你以前必定遇到过主题定制的需求。支持主题定制的站点让用户有了自定义的体验,感受站点在本身的掌控之中。

下面是我写的一个简单示例:

使用 CSS 变量来实现有多么容易呢?

咱们来看看。

在此以前,我想提醒你,这个示例很是重要。经过这个示例我将引导你理解使用 JavaScript 更新 CSS 变量的思想。

很是好玩!

你会爱上它的!

咱们究竟想作什么。

CSS 变量的美在于其本质是响应式的。一旦 CSS 变量更新了,任意带有 CSS 变量的属性的值也都会随之更新。

从概念上讲,下面这张图解释了这个示例的流程。

流程。

所以,咱们须要给点击事件监听器写一些 JavaScript 代码。

在这个简单的示例里,文本与页面的颜色和背景色都是基于 CSS 变量的。

当你点击页面上方的按钮时,JavaScript 会将 CSS 变量中的颜色切换成别的颜色,页面的背景色也就随之更新。

这就是所有了。

还有一件事。

当我说 CSS 变量切换成别的颜色时,是怎么作到的呢?

行内设置变量。

即便是在行内设置,CSS 变量也会生效。在 JavaScript 中,咱们控制了文档的根节点,而后就能够在行内给 CSS 变量设置新的值了。

明白了吗?

咱们说了太多了,如今该干些实际的了。

结构初始化

初始化结构是这样的:

<div class="theme">
  <button value="dark">dark</button>
  <button value="calm">calm</button>
  <button value="light">light</button>
</div>

<article>
...
</article>
复制代码

结构中有三个父元素为 .theme 的按钮元素。为了看起来尽量简短,我将 article 元素内的内容截断了。article 元素内就是页面的内容。

设置页面样式

项目的成功始于页面的样式。这个技巧很是简单。

咱们设置页面样式的 background-colorcolor 是基于变量的,而不是写死的属性值。

这就是我说的:

body {
  background-color: var(--bg, white);
  color: var(--bg-text, black)
}
复制代码

这么作的缘由显而易见。不管什么时候按钮被点击,咱们都会改变文档中两个变量的值。

根据变量值的改变,页面的总体样式也就随之更新。小菜一碟。

让咱们继续前进,解决在 JavaScript 中更新属性值的问题。

进入 JavaScript

我将直接把这个项目所需的所有 JavaScript 展现出来。

const root = document.documentElement
const themeBtns = document.querySelectorAll('.theme > button')

themeBtns.forEach((btn) => {
  btn.addEventListener('click', handleThemeUpdate)
})

function handleThemeUpdate(e) {
  switch(e.target.value) {
    case 'dark':
      root.style.setProperty('--bg', 'black')
      root.style.setProperty('--bg-text', 'white')
      break
    case 'calm':
       root.style.setProperty('--bg', '#B3E5FC')
       root.style.setProperty('--bg-text', '#37474F')
      break
    case 'light':
      root.style.setProperty('--bg', 'white')
      root.style.setProperty('--bg-text', 'black')
      break
  }
}
复制代码

不要被这段代码吓到,它比你想象的要简单。

首先,保存一份对根节点的引用, const root = document.documentElement

这里的根节点就是 HTML 元素。你很快就会明白为何这很重要。若是你很好奇的话,我能够先告诉你一点,给 CSS 变量设置新值时须要根节点。

一样地,保存一份对按钮的引用, const themeBtns = document.querySelectorAll('.theme > button')

querySelectorAll 生成的数据是能够进行遍历的类数组结构。遍历按钮,而后给按钮设置点击事件监听。

这里是怎么作:

themeBtns.forEach((btn) => {
  btn.addEventListener('click', handleThemeUpdate)
})
复制代码

handleThemeUpdate 函数去哪了?咱们接下来就会讨论这个函数。

每一个按钮被点击后,都会调用回调函数 handleThemeUpdate。所以知道是哪一个按钮被点击以及后续该执行什么正确操做很重要。

鉴于此,咱们使用了 switch 操做符,基于被点击的按钮的值来执行不一样的操做。

接下来再看一遍这段 JavaScript 代码,你会理解地更好一些。

项目 3: 构建 CSS 变量展位

避免你错过它,这是咱们即将构建的项目:

请记住盒子的颜色是动态更新的,以及盒子容器是随着输入范围值的变化进行 3D 旋转的。

你能够直接在 Codepen 上玩一下这个项目。

这是使用 JavaScript 更新 CSS 变量以及随之而来的响应式特性的绝佳示例。

让咱们来看看如何来构建。

结构

如下是所需的组件。

  1. 一个范围输入框
  2. 一个装载使用说明文字的容器
  3. 一个装载盒子列表的 section,每一个盒子包含输入框

结构变得很简单。

如下就是:

<main class="booth">
  <aside class="slider">
    <label>Move this 👇 </label>
    <input class="booth-slider" type="range" min="-50" max="50" value="-50" step="5"/>
  </aside>

  <section class="color-boxes">
    <div class="color-box" id="1"><input value="red"/></div>
    <div class="color-box" id="2"><input/></div>
    <div class="color-box" id="3"><input/></div>
    <div class="color-box" id="4"><input/></div>
    <div class="color-box" id="5"><input/></div>
    <div class="color-box" id="6"><input/></div>
  </section>

  <footer class="instructions">
    👉🏻 Move the slider<br/>
    👉🏻 Write any color in the red boxes
  </footer>
</main>
复制代码

如下几件事须要注意。

  1. 范围输入表明了从 -5050 范围的值,step 值为 5。所以范围输入的最小值就是 -50
  2. 若是你并不肯定范围输入是否能够运行,能够在 w3schools 上检查如下
  3. 注意类名为 .color-boxes 的 section 是如何包含其它 .color-box 容器的。这些容器中包含输入框。
  4. 第一个输入框有默认值为 red。

理解了文档结构后,给它添加样式:

  1. .slider.instructions 设置为脱离文档流,将它们的 position 设置为 absolute
  2. body 元素的背景色设置为日出的颜色,并在左下角用花朵做装饰
  3. color-boxes 容器定位到中间
  4. color-boxes 容器添加样式

让咱们把这些任务都完成。

如下代码会完成第一步。

/* Slider */
.slider,
.instructions {
  position: absolute;
  background: rgba(0,0,0,0.4);
  padding: 1rem 2rem;
  border-radius: 5px
}
.slider {
  right: 10px;
  top: 10px;
}
.slider > * {
  display: block;
}


/* Instructions */
.instructions {
  text-align: center;
  bottom: 0;
  background: initial;
  color: black;
}
复制代码

这段代码并不像你想的那般复杂。但愿你能通读一遍并能读懂,若是没有的话,能够留下评论或者发个 twitter。

body 添加样式会涉及到更多内容,但愿你足够了解 CSS。

既然咱们想用背景颜色和背景图来设置元素的样式,那么使用 background 简写属性设置多个背景属性多是最佳选择。

就是这样的:

body {
  margin: 0;
  color: rgba(255,255,255,0.9);
  background: url('http://bit.ly/2FiPrRA') 0 100%/340px no-repeat, var(--primary-color);
  font-family: 'Shadows Into Light Two', cursive;
}
复制代码

url 是向日葵图片的连接。

接下来设置的 0 100% 表明图片在背景中的位置。

这个插图展现了 CSS 的 background position 属性是如何工做的:

来自于: CSS 进阶指南

来自于: CSS 进阶指南

正斜杠后面的表明 background-size 被设置为 340px,若是将它设置得更小的话,图片也会变得更小。

no-repeat,你可能已经猜到它是作什么的。它避免背景图片自我复制,铺满背景。

最后,跟在逗号后面的是第二个背景属性声明。这一次,咱们仅仅将 background-color 设置为 var(primary-color)

哇,这是个变量。

这意味着你必须定义变量。 就是这样:

:root {
  --primary-color: rgba(241,196,15 ,1)
}
复制代码

这里讲主题色设置为日出黄。没什么大问题。立刻,咱们就会在这里设置更多的变量。

如今,咱们将 color-boxes 定位到中间

main.booth {
  min-height: 100vh;

  display: flex;
  justify-content: center;
  align-items: center;
}
复制代码

主容器充当 flex 容器,它的子元素会正确地被定位到页面中间。也就是说咱们的 color-box 容器会被定位到页面中间。

咱们把 color-boxes 以及它的子元素容器变得更好看一些。

首先,是子元素:

.color-box {
  padding: 1rem 3.5rem;
  margin-bottom: 0.5rem;
  border: 1px solid rgba(255,255,255,0.2);
  border-radius: 0.3rem;
  box-shadow: 10px 10px 30px rgba(0,0,0,0.4);
}
复制代码

这就加上了好看的阴影,使得效果更酷炫了。

还没结束,咱们给总体的 container-boxes 容器加上样式:

/* Color Boxes */
.color-boxes {
  background: var(--secondary-color);
  box-shadow: 10px 10px 30px rgba(0,0,0,0.4);
  border-radius: 0.3rem;

  transform: perspective(500px) rotateY( calc(var(--slider) * 1deg));
  transition: transform 0.3s
}
复制代码

哇!

变得太复杂了。

去掉一些。

变得简单点:

.color-boxes {
   background: var(--secondary-color);
   box-shadow: 10px 10px 30px rgba(0,0,0,0.4);
   border-radius: 0.3rem;
}
复制代码

你知道效果会变成什么样,对吧?

这里有个新变量,须要在根元素中声明添加进来。

:root {
  --primary-color: rgba(241,196,15 ,1);
  --secondary-color: red;
}
复制代码

第二个颜色是红色,咱们会给容器加上红色的背景。

接下来这部分可能会让你以为难以理解:

/* Color Boxes */
.color-boxes {
  transform: perspective(500px) rotateY( calc(var(--slider) * 1deg));
  transition: transform 0.3s
}
复制代码

又是咱们会将 transform 的属性值简写成上面这样。

举个例子:

transform: perspective(500px) rotateY( 30deg);
复制代码

这个 transform 简写用了两个不一样的函数。一个是视角,另外一个是沿着 Y 轴旋转。

那么 perspective 函数 和 rotateY 函数是作什么的呢?

perspective() 函数应用于 3D 空间内旋转的元素。它激活了三维空间,并沿 z 轴给出元素的深度。

能够在 codrops 上阅读更多有关 perspective 的知识。

rotateY 函数是干什么的?

激活三维空间后,元素具备了 x,y,z 轴。rotateY 就是元素围绕 Y 平面旋转。

下面这个 codrops 的图对于视觉化理解颇有帮助。

Codrops

我但愿这能让你更明白一些。

回到以前的话题。

当你回到这里,你知道哪一个函数影响 .container-box 的旋转了吗?

是 rotateY 函数使得盒子沿着 Y 周旋转。

因为传入 rotateY 函数的值将被 JavaScript 更新,这个值也将经过变量来传入。

为何要给变量乘上 1deg?

做为通常的经验法则,为了显式地更灵活,建议在构建单独符号时变量中储存没有单位的值。

经过 calc 函数,你能够用乘法将它们转化成任何单位。

这意味着你能够随心所欲。将做为比例的 deg 转换为视窗单位 vw 也能够。

在这个场景中,咱们经过 “数字” 乘上 1deg 将数字转换成角度

因为 CSS 不懂数学,你须要将公式传入 calc 函数,这样 CSS 才能正确计算。

完成以后咱们就能够继续了。咱们能够在 JavaScript 中用各类方法来更新它。

如今,只剩下一点点的 CSS 代码须要写了。

就是这些:

/* 给每一个盒子添加颜色 */
.color-box:nth-child(1) {
  background: var(--bg-1)
}
.color-box:nth-child(2) {
  background: var(--bg-2)
}
.color-box:nth-child(3) {
  background: var(--bg-3)
}
.color-box:nth-child(4) {
  background: var(--bg-4)
}
.color-box:nth-child(5) {
  background: var(--bg-5)
}
.color-box:nth-child(6) {
  background: var(--bg-6)
}
复制代码

这些奇怪的东西是什么?

首先,nth-child 选择器用来选择子盒子。

这里须要一些前瞻。咱们知道,每一个盒子的背景色都会更新。咱们也知道背景色须要用变量表示,以在 JavaScript 中更新。对吧?

接下来:

.color-box:nth-child(1) {
  background: var(--bg-1)
}
复制代码

简单吧。

这里有个问题。若是变量不存在的话怎么办?

咱们一个回退方式。

这是可行的:

.color-box:nth-child(1) {
  background: var(--bg-1, red)
}
复制代码

在这个特殊实例中,我选择不提供任何回退方式。

若是某个属性值中使用的变量非法,属性将使用其初始值。

所以,当 --bg-1 非法或者不可用时,背景色会默认切换成它的初始颜色或者透明。

初始值指向属性还未显式设置时的值。好比说,若是你没有给元素设置 background-color 属性的话,它的背景色会默认为 transparent

初始值是一种默认属性值。

写点 JavaScript

在 JavaScript 这一边须要作的事情不多。

首先要处理一下 slider。

仅仅五行代码就能够!

const root = document.documentElement
const range = document.querySelector('.booth-slider')

// 一旦 slider 的范围值发生变化,就执行回调
range.addEventListener('input', handleSlider)

function handleSlider (e) {
  let value = e.target.value
  root.style.setProperty('--slider', value)
}
复制代码

这很简单,对吧?

我来解释一下。

首先,保存一份 slider 元素的引用,const range = document.querySelector('.booth-slider')

设置一个事件监听器,一旦范围输入值发生变化就会触发,range.addEventListener('input', handleSlider)

写一个回调函数, handleSlider

function handleSlider (e) {
  let value = e.target.value
  root.style.setProperty('--slider', value)
}
复制代码

root.style.setProperty('--slider', value) 的意思是获取 root 元素(HTML),读取它的样式,并给它设置属性。

处理颜色变化

这与处理 slider 值的变化同样简单。

这么作就能够:

const inputs = document.querySelectorAll('.color-box > input')

// 一旦输入值发生变化,执行回调
inputs.forEach(input => {
  input.addEventListener('input', handleInputChange)
})

function handleInputChange (e) {
  let value = e.target.value
  let inputId = e.target.parentNode.id
  let inputBg = `--bg-${inputId}`
  root.style.setProperty(inputBg, value)
}
复制代码

保存一份全部文本输入的引用, const inputs = document.querySelectorAll('.color-box > input')

给每一个输入框加上事件监听:

inputs.forEach(input => {
   input.addEventListener('input', handleInputChange)
})
复制代码

handleInputChange 函数:

function handleInputChange (e) {
  let value = e.target.value
  let inputId = e.target.parentNode.id
  let inputBg = `--bg-${inputId}`
  root.style.setProperty(inputBg, value)
}
复制代码

嗯……

就是这些!

项目完成了。

我遗漏了什么?

当我完成并修改了初稿后才发现我没有提到浏览器支持。那让我来处理这个烂摊子。

对于 CSS 变量的(又名自定义属性)浏览器支持并不差。 浏览器支持性很是好,基本全部的现代浏览器都支持良好(本文写做时已超过 87%)。

caniuse

那么,你能够在生产环境使用 CSS 变量吗?固然能够!可是这多大程度上适用于你还需本身判断。

好的一面是,你可使用像 Myth 这样的预处理器来使用 CSS 变量。它将“将来的” CSS 预编译成如今你就可使用的代码,是否是很赞?

若是你有使用 postCSS 的经验, 这也一样是一个好方法。这是 postCSS 的 CSS 变量模块

就这些,我已所有写完。

很差,我遇到了问题!

购买电子书 能够线上阅读, 还能得到 私人的 slack 邀请,你能够向我咨询任何问题。

这是个公平交易,对吧?

稍后联系! 💕


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏