Blog
Blog copied to clipboard
了解 CSS 变量
了解 CSS 变量
如果用过 less\scss\stylus 等预处理 CSS 语言,那么你就不会对使用变量来简化我们的 CSS 开发工作感到陌生。但是你知道吗?CSS 现在也支持原生的变量了: var()。
基本用法
CSS 原生的变量如何使用?我们来看下:
.selector {
--size: 16px;
font-size: var(--size)
}
这个简单的例子中,.selector 的 fontSize 值就是 16px。但是这么简单的例子凸显不出 CSS 变量的价值,我们来看个更能体现变量价值的例子:
body {--bg: #fff}
p {background: var(--bg)}
a {color: var(--bg)}
span {border: 1px solid var(--bg)}
这个例子中,我们在 body 元素中定义了变量 --bg,并在其他三个标签上应用了变量,而且用于不同的 CSS 属性。变量无疑给我们开发 CSS 带来了便利和更好的维护性,简单修改变量值即可同时在不同 selector 和不同属性上生效。那么我们接下来再仔细看看 CSS 变量的一些特性。
变量声明
--* 格式
CSS 变量有一个很明显的特点就是必须以 -- 起始,看起来很奇怪。早期的规范是以 var- 作为起始,所以在一些老版本浏览器中可能需要定义 var- 起始(firefox 31 以下 bug 985838)的变量名才能生效。
我个人感觉有一个好处就是官方钦定了 CSS 变量名的烤肉串风格(Kebabs Style)写法(人都给你两个 - 了)。
大小写敏感
和普通的 CSS 属性忽略大小写不同,变量名对大小写是敏感的。
body {
--color: #f90;
--Color: #f00;
background: var(--color); /* #f90 */
}
建议变量名全小写,原因就是上面我们提到的,变量名使用烤肉串风格声明。
变量必须声明在样式规则中
不同于预处理语法直接声明变量,CSS 变量必须声明在样式规则中,包括条件化规则 @media 等。
--size: 20px; /* 语法错误 */
body {
--size: 20px; /* 正确声明 */
}
但是在 @keyframes 中定义的变量会被作为动画属性。因为规范规定变量是 Animatable: no 的,不可以作为动画属性的。一旦在 @keyframes 中定义了变量,且有动画属性使用了该变量,那么这个属性将会受到影响,导致动画失效。
@keyframe test {
from { --color: #f00; background: var(--color)}
to { --color: #fff; background: var(--color)}
}
这种方式的写法,背景色不会出现变化哦。那该怎么做呢?一种方式就是多定义几个变量,比如:
@keyframe test {
from { background: var(--color-start)}
to { background: var(--color-end)}
}
引用变量
变量必须通过将变量名放入 var() 中进行引用,否则会被忽略。
body {
--color: #f90;
background: --color; /* 语法错误 */
background: var(--color) /* 正确 */
}
继承与级联优先级
CSS 变量也遵循 CSS 的继承规则和级联优先级规则。比如,当一个规则使用了变量,但是自身没有定义该变量时,CSS 解析器会向上查找变量,试图使用父级、祖父级的变量。
.parent {--size: 20px}
.parent .current {font-size: var(--size)} /* current 的规则并没有定义 --szie 变量,使用的是继承到的变量 */
当多条规则中有重复定义的变量时,解析器会按样式级联优先级来确定使用哪个值:
.current.more {--size: 20px;} /* 这条规则权重大,所以 --size 变量取值为 20px */
.current {--size: 10px}
注意没有继承关系时,可以存在多个同名变量。
<div class="one"></div>
<div class="two"></div>
<div class="three"></div>
.one {--size: 10px; font-size: var(--size)}
.two {--size: 20px; font-size: var(--size)}
.three {--size: 30px; font-size: var(--size)}
/*等同于*/
.one {font-size: 10px}
.two {font-size: 20px}
.three {font-size: 30px}
默认值
如果需要默认值,可以在 var 方法中传入哦。
body {background: var(--bg, #f00)}
如果没有找到 --bg 变量,那么 #f00 会生效。
需要注意的是,在使用变量时可能出现非法值的情况:
body {--bg: 20px; background: var(--bg, #f00)}
这种情况下,规则会被解析为:
body {background: 20px}
最终 body 的背景色是透明,而不是我们在 var 中设置的默认值。也就是说,CSS 变量的默认值只在变量未声明的情况下生效,不会影响值与属性非法组合情况。
变量提升
和 JS 中定义变量类似,CSS 变量也拥有变量提升的效果,但是也有差异。
console.log(a) // undefined
var a = 1;
console.log(a) // 1
//等同于
var a;
console.log(a)
a = 1;
console.log(a)
JS 中变量只有声明会被提升,值还是按照正常的代码流程进行赋值。而 CSS 变量不仅声明被提升,值也会被提升。
body {background: var(--color); --color: #f00}
/* 等同于 */
body {--color: #f00; background: var(--color)}
在定义变量之前使用变量,和定义变量之后使用变量效果相同。
变量赋值
上面的例子中我们都在 CSS 属性值的位置使用变量,那么我们可以将变量作为 CSS 属性吗?比如:
--prop : font-size;
var(--prop): 12px
答案是:不可以。
虽然CSS 变量不能作为属性名,但是它可以使用另一个变量进行赋值或表达式计算:
body {
--size: 10px;
--big-size: calc(var(--size) * 2);
}
此处,--big-size 变量的值为 20px。
赋值的时候我们需要注意给变量带上单位,如果在引用之后加上单位是不能正确解析的,如:
body {
--size: 10;
font-size: var(--size)px; /* 会被解析成 font-size: 10 px,数字 10 与 px 之间有一个空格 */
}
这种写法等同于给 CSS 属性设置了一个非法值。
CSSOM 进行动态设置 CSS 变量
规范中有提到可以使用 CSSOM 进行变量的获取与设置:
element.style.setProperty('--foo', '10px')
element.style.setProperty('height', 'var(--foo)') // 设置元素 height 为 10px
element.style.getPropertyValue('--foo') // 获取变量值,返回 10px
我们可以通过 DOM 对象的 style 属性进行 CSS 变量的取值和设置。
需要注意的事,只有 inline 到 DOM 对象的 CSS 变量能通过这种方式获取,写在 CSS 文件或 style 元素中的 CSS 变量的获取方式尚未找到,有知晓的朋友欢迎留言相告,感谢。
兼容性
目前来看,CSS 变量在 PC 浏览器兼容性比较好,只有 IE 尚未实现。而移动端 IOS safari 已经实现了,安卓需要再等等就可以在生产使用。
当你看到这篇文章的时候可能有所变化,请前往 can i use 查看实时数据。
参考文献
- https://drafts.csswg.org/css-variables/#syntax
- https://github.com/w3c/csswg-drafts/issues/580
- http://www.zhangxinxu.com/wordpress/2016/11/css-css3-variables-var/comment-page-1/