变量与自定义属性

自定义属性 | Custom Properties/Variables

大部分的编程语言都有所谓变量的概念,早期我们在 CSS 中往往是利用 SCSS 或者 LESS 这样的预处理器中提供的变量特性;令人欣慰的是,CSS 最终也支持自定义变量的概念。CSS Custom Properties 或者所谓的 CSS Variables,对于前端开发者而言能够减少重复代码量,提升代码的可读性与灵活性;并且其是 DOM 的一部分,不再需要额外的编译步骤,并且在性能上也有所提升。随着应用复杂度的增加,我们的 CSS 样式文件也会变得非常庞杂,而将所有的常量定义在独立的文件中,改变样式的时候,我们就可以避免在大量的代码中重复地搜索与修改。

CSS Variables 也逐步被应用到应用或者组件库的开发中,譬如 basicScroll 这样的组件库即是利用 CSS Variables 来进行变换控制。

语法基础

顾名思义,CSS Custom Properties/CSS Variables 并非独立的变量定义,而是对于元素定义特殊的属性,这种属性的定义可以作用于任意的元素;CSS Variables 基本的定义方式如下:

/* 传统方式 */
#title {
  color: #ff6f69;
}

.quote {
  color: #ff6f69;
}

/* 统一变量 */
:root {
  --red: #ff6f69;
}

#title {
  color: var(--red);
}

.quote {
  color: var(--red);
}

既然是变量,我们肯定需要考虑其作用域;如上面所示的声明在 :root 伪类中的变量,等价于绑定在了 DOM 树的根元素(一般来说是 <html>),其作用域即是所谓的全局作用域。而相对的局部作用域,即意味着某个变量仅可以被其子元素选择器所访问。譬如在某个自定义弹窗中:

.alert {
  --alert-color: #ff6f69;
}

// 在其子元素中使用
.alert p {
  color: var(--alert-color);
  border: 1px solid var(--alert-color);
}

上述定义的 alert-color 在应用中的其他部分就无法使用。对于需要进行计算的属性值,同样可以联合使用 calc 与 var:

.margin {
  --space: calc(20px * 2);
  font-size: var(--space); /*equals 40px*/
}

另一方面,CSS Variables 能够简化响应式界面开发,譬如我们可以在媒介查询中设置变量的尺寸值,这样就避免了编写大量重复的代码:

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

CSS Variables 可以通过 HTML 元素的 style 属性进行定义:

<!--HTML-->
<html style="--color: red">
  <!--CSS-->
  body { color: var(--color) }
</html>

同样,我们也可以使用 DOM API 进行变量控制。

// 设置变量值
element.style.setProperty("--my-color", "rebeccapurple");

// 获取变量值
element.style.getPropertyValue("--my-color");
// => 'rebeccapurple'

// 移除变量值
element.style.removeProperty("--my-color");

在实际的使用中,我们还需要避免循环依赖的出现。当设置某个变量依赖其本身,或者两个变量互相依赖时,就出现了循环依赖:

/* 引用自身 */
:root {
  --m: var(--m);
}

body {
  margin: var(--m);
}

/* 相互依赖 */
:root {
  --one: calc(var(--two) + 10px);
  --two: calc(var(--one) - 10px);
}

典型应用

CSS Variables Site Theming

这篇文章中也讨论了如何利用 CSS Variables 与 Flexbox 混合实现的动态排序功能。

<tr
  class="table-row"
  style="
    --order-by-published: 161221;
    --order-by-views: 2431;
  "
>
  <th class="table-cell">
    <a href="http://kizu.ru/en/fun/controlling-the-specificity/"
      >Controlling the Specificity</a
    >
  </th>
  <td class="table-cell">2016-12-21</td>
  <td class="table-cell">2 431</td>
</tr>
.table-body {
  display: flex;
  flex-direction: column;
}

.table-row {
  order: calc(var(--order) * var(--sort-order, -1));
}

#sort-by-published:checked ~ .table > .table-body > .table-row {
  --order: var(--order-by-published);
}

#sort-by-views:checked ~ .table > .table-body > .table-row {
  --order: var(--order-by-views);
}

#sort-ascending:checked + .table {
  --sort-order: 1;
}

#sort-by-name:checked ~ #sort-ascending:checked + .table > .table-body {
  flex-direction: column-reverse;
}
上一页
下一页