变量与自定义属性
自定义属性 | 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 与 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;
}