暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

CSS 变量的 10 个怪异用法

前端新世界 2022-02-16
462
喜欢就关注我们吧


本文翻译自:
https://dev.to/afif/what-no-one-told-you-about-css-variables-553o

CSS变量很棒,但是你真的了解有关CSS变量的所有信息吗?

在这篇文章中,我将重点介绍一些关于CSS变量的怪异之处。希望看完这篇文章,你会对它们大为改观。

CSS变量的怪异之处,有:

  1. 小心使用!important
  2. 无法存储网址
  3. 可以使无效值有效
  4. 可以不带单位
  5. 可以使用动画
  6. 无法存储inherit
  7. 可为空
  8. CSS变量不是C++变量
  9. 只从父级到子级起作用
  10. 可以使用奇怪的语法

1)小心使用 !important

在CSS变量中使用!important
有点棘手,所以让我们先从一个基础示例开始:

p {
  --color:red!important;

  color:var(--color);
  color:blue;
}

p
的颜色是什么?你认为它为red
,是因为有以下代码:

p {
  color:red!important;
  color:blue;
}

回答错误!p
的颜色是蓝色的,因为:

p {
  color:red;
  color:blue;
}

在这种情况下,!important
不是color值的一部分,而是用于增加--color
的特异性。下面这段解释摘自技术规范:

注意:CSS变量(自定义属性)末尾可以包含!important
,但是CSS解析器会自动将其从属性的值中删除,并使CSS级联中的自定义属性变得重要。换句话说,对顶层“!
”字符的禁止不会阻止!important
的使用,因为!important
在进行语法检查之前已被删除。

下面是另一个更易于理解的示例:

p{
  --color:red!important;
  --color:blue; 

  color:var(--color);
}

这次的结果就是red
了:

1.我们有两个相同属性--color
,因此我们需要解决级联。因为第一个有!important
,所以它赢了。

2.因为--color:red!important
赢了,所以!important
被删除,然后将值应用于color

3.我们有color:red

让我们将代码修改成如下:

p{
  --color:red!important;
  --color:blue; 

  color:var(--color);
  color:blue;
}

按照相同的逻辑,我们先为--color
color
解决优先级。--color:red!important
赢了,color:blue
也是如此,所以最后我们得到blue
,因为我们不再关注color:var(--color)
了。

一个重要的规则是始终将CSS变量(自定义属性)视为普通属性,而不仅仅是存储值的变量。

自定义属性是普通属性,因此可以在任何元素上声明它们,使用常规继承和级联规则进行解析,可以使用@media
和其他条件规则进行条件设置,可以在HTML的style
属性中使用,可以读取或设置使用CSSOM等。

2)无法存储网址

这可能是你经常会遇到的限制。

错误做法 ❌

:root {
  --url:"https://picsum.photos/id/1/200/300";
}
.box {
  background:url(var(--url));
}

正确做法 ✔️

:root {
  --url:url("https://picsum.photos/id/1/200/300");
}
.box {
  background:var(--url);
}

此限制与url()
的解析方式有关。解释起来有些麻烦,但正如我们看到的那样,要修复非常容易。只要在CSS变量中添加url()
部分即可。

3)可以使无效值有效!

这是我最喜欢的一点,也是很让人头痛的地方。

还是从一个基础的例子开始:

.box {
  background: red;
  backgroundlinaer-gradient(red, blue);
}

我们的.box
将有渐变色...不对,实际上是只有red背景。啊呀!linear-*
中打错字了。我之所以这么快注意到我的错误,是因为浏览器跳过了此声明,使用了前一个声明。

现在,让我们介绍一个变量:

.box {
  --color:red;
  backgroundvar(--color);
  backgroundlinaer-gradient(var(--color), blue);
}

测试代码后你会发现,背景现在是透明的,并且我们的第二个声明不再跳过,因为它现在是有效的了。你甚至会发现第一个声明才是跳过的那个,因为第二个声明将其覆盖了。

这到底是怎么回事??明明第二个属性中linaer-gradient
拼写错误了。

当在属性中使用变量时,浏览器将仅在“计算值时间”中计算此属性的值,因为我们首先需要了解变量的内容。在这种情况下,浏览器在进行级联时将认为该值有效,只有到后来它才会变得无效。

在我们的例子中,浏览器在解决级联之后考虑最后一个声明。然后,在进行计算时,它似乎无效的,因此将被忽略。因为已经解决了级联,并且最后不是透明的背景,所以我们将不再返回上一个声明。

你可能会认为这种行为是不合逻辑的,但实际上是合乎逻辑的,因为基于CSS变量,值可以是有效也可以是无效的,因此浏览器刚开始是不知道的。

.box {
  --color:10px/* a "valid" variable */
  background: red; /* a "valid" declaration */
  background:linear-gradient(var(--color),blue); /* a "valid" declaration that will override the first one  */
  /* The result is an "invalid" value ... */ 
}

如果声明包含以初始值引用自定义属性的var()
,则声明在计算值时可以是无效的,如上所述,或者声明使用有效的自定义属性,但是在替换了var()
函数之后,属性值无效。发生这种情况时,属性的计算值是属性的继承值还是初始值取决于该属性是否被继承,就好像该属性的值已被指定为unset
关键字一样。

用简单的话来说:CSS变量将在备用模式下使用属性的状态,直到我们进行计算为止。只有在计算之后,我们才能说它是有效还是无效的。如果无效,那就太迟了,我们不能再回过头去使用另一个了。

4)可以不带单位

几乎所有的教程/课程都会展示类似这样的例子:

:root {
 --p10px;
}
.box {
  paddingvar(--p);
}

但是你也可以执行如下操作:

:root {
 --p10;
}
.box {
  paddingcalc(var(--p)*1px);
}

在变量中提供单位不是强制性的,在某些情况下,最好使用无单位值,因为添加单位相当容易,而我们可能需要对不同的单位使用相同的值。

举一个例子

HTML:

<div class="box">
  <div class="content">
    CONTENT<br> Some more Content
  </div>
</div>

<div class="box" style="--b:3;--c:10">
  <div class="content">
    CONTENT<br> more Content
  </div>
</div>

<div class="box" style="--b:1;--c:40">
  <div class="content">
    AA BB
  </div>
</div>
<div class="box" style="--b:1;--c:5">
  <div class="content">
    AA BB
  </div>
</div>

CSS:

.box {
  --b:2;   /* border length (without unit!!)*/
  --c:25;  /* the cube perspective (without unit!!)*/
  
  --g:calc((var(--c) - var(--b))*0.707px); /* 0.707 = cos(45deg) = sin(45deg) */
  margin10px;
  display: inline-block;
  border-style:solid;
  border-width:calc(var(--b)*1pxcalc(var(--b)*1pxcalc(var(--c)*1pxcalc(var(--c)*1px);
  border-image-slice:var(--b) var(--b) var(--c) var(--c);
  border-image-source:linear-gradient(-45deg,transparent var(--g),red 0 calc(100% - var(--g)),transparent 0);
}

.content {
  padding5px;
  font-size:40px;
}

千万不要忘记这个重要功能。或许有一天它会成为你的救星。

5)可以使用动画

最初,按照规范,CSS变量被定义为非动画属性:

Animatable: no

但是事情发生了变化。得亏于新的@property
,我们可以使用CSS变量进行动画/过渡。

但浏览器支持率仍然很低(尤其是Firefox),但是现在我们应该要了解这个知识点了。

6)不能存储inherit值

请看以下示例:

<div class="box">
  <div class="item"></div>
</div>

.box {
  border:2px solid red;
}
.item {
  --b:inherit;
  border:var(--b);
}

凭直觉,我们可能认为.item
将继承其父元素的相同边框,因为--b
包含inherit
,但其实不然(你可以试试看)。

正如我在(1)中所解释的那样,常见的错误是认为CSS变量将仅存储我们以后可以使用的值。CSS变量(自定义属性)是普通属性,因此可以应用inherit
,但不能存储。

例子:

.box {
  --b:5px solid blue; /* we define the variable on the parent */
}
.item {
  --b:inherit; /* the child will inherit the same value so "5px solid blue"*/
  border:var(--b); /* we will have "5px solid blue" */
}

如你所见,继承逻辑的应用方式与普通属性相同。

值得注意的是,上述操作是无用的,因为默认情况下CSS变量是继承的。就像将inherit
设置为默认情况下被继承的属性(例如color)一样。

顺便说一句,其他关键字,例如unset
revert
,适用相同的逻辑。

7)可以为空

是的,你可以执行以下操作:

.box {
  --color: ;
  background:var(--color); 
}

根据技术规范,上述代码有效:

注意:虽然<declaration-value>
必须代表至少一个令牌,但是令牌可以是空格。这意味着--foo: ;
是有效的,并且相应的var(--foo)
调用将使用单个空格作为其替换值,但--foo:;
是无效的。

注意最后一句话,原因是因为我们需要至少有一个空格。以下是无效的:

.box {
  --color:;
  background:var(--color); 
}

此怪异之处主要与fallback功能结合使用来做一些魔术。

用一个基本的例子来理解这个技巧:

.box {
  background:
   linear-gradient(blue,transparent)
   var(--color,red); 
}

<div class="box">
  I will have `background:linear-gradient(blue,transparent) red;`
</div>
<div class="box" style="--color:green">
  I will have `background:linear-gradient(blue,transparent) green;`
</div>
<div class="box" style="--color: ;">
  I will have `background:linear-gradient(blue,transparent)  ;`
</div>

1.第一个box没有定义变量,因此将使用后备。

2.第二个box定义了一个变量,因此使用这个。

3.最后一个定义了一个空变量,因此使用了空值。就像我们不再有var(--color,red)
那样。

空值使我们可以从属性中删除var()
声明!在复杂值内使用var()
时,这很有用。

如果单独使用var()
,则适用相同的逻辑,但最终将得到一个空值,该值对于大多数属性都是无效的。

如果我们以第一个例子为例,我们有background: ;
,这会在“计算值时”导致成为无效值(看第(3)点),因此background是透明的。

8)CSS变量不是C++变量

不幸的是,许多开发人员往往将CSS变量与其他语言的变量进行比较,然后在逻辑上遇到很多问题。对于此特殊原因,我就不将它们称为变量,而将其称为“自定义”属性,因为它们是属性。

大家想这样做:

:root {
  --p:5px;
  --p:calc(var(--p) + 1px); /* let's increment by 1px */
}

:root {
  --x5px;
  --y10px;
  /* let's do a variable switch */
  --cvar(--y);
  --yvar(--x);
  --xvar(--c);
}

.box {
  --s10px;
  margin:var(--s); /* I want 10px of margin */
  --s20px;
  padding:var(--s): /* then 20px of padding */
}

以上所有内容将永远无法起作用。前两个无效,因为有一个循环依赖性:变量引用了自身(第一个示例),或者一组变量创建了循环(第二个示例)。

在最后一个示例中,padding
margin
都将为20px,因为级联会将最后一个声明--s: 20px
设置为高优先级,这将同时应用于margin
padding

也就是说,在使用CSS变量时,你应该不考虑C++,Javascript,Java等,因为它们是有自己逻辑的自定义属性。

9)只从父级到子级起作用。

记住这条黄金法则:CSS变量总是从父元素(或祖先)行进到子元素。它们从不从子代到父代或兄弟元素之间行进。

这会导致我们出现以下错误:

:root {
  --c1: red;
  --c2: blue;
  --gradlinear-gradient(var(--c1),var(--c2);
}
.box {
  --c1: green;
  background:var(--grad);
}

你认为.box
的背景是linear-gradient(green, blue)
?不,它是linear-gradient(red, blue)

根元素是DOM中最上层的元素,因此它是box元素的祖先,黄金法则说明只能是父->子,因此--c1
不能朝相反的方向到达根元素,改变--grad
,然后回到另一个方向重新发送更改值--grad

在这个示例中,.box
将继承用root
内部的--c1
--c2
值定义的--grad
值。更改--c1
只会更改.box
--c1
的值,仅此而已。

10)可以使用奇怪的语法

最后一个有趣的奇异之处。

你知道你可以执行以下操作吗?

body {
  --:red;
  background:var(--);
}

太赞了!是的,只用两个破折号就能来定义CSS变量。

你觉得上面的代码很疯狂,那么请看以下内容:

body {
 --📕:red;
 --📗:green; 
 --📘:blue;
 --📙:orange;
}

是的,有了表情符号!你可以使用表情符号定义变量,并且可以正常工作。

CSS变量的语法几乎怎么写都可以,唯一的要求就是以--
开头。你也可以以数字开头(例如:--1
:)。

为什么不只是破折号或相同的变量存储两个不同的值呢:

body {
  ---------:red;
  background:var(---------);
}

还是存储两个不同值的同一个变量:

body {
  --‎:red;
  --‎:blue;
  background:linear-gradient(90deg, var(--‎),var(--‎));
}

试一下,你会发现得到的是渐变色!

当然,除非你想让同事发疯,否则永远不要在真实的项目中这样做。

总结

感谢大家的阅读,希望能对大家有所帮助。

(文本完)

每日分享前端插件干货,欢迎关注!

点赞和在看就是最大的支持❤️

文章转载自前端新世界,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论