關於 CSS 變數的一些你可能不瞭解的事!

2022-04-21 13:00:41
本篇文章帶大家瞭解一下 變數,介紹下沒人告訴你關於 CSS 變數的那些事。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有所幫助。

CSS 變數很不錯哦,但是你知道它們的詳情?【推薦學習:】

1. 小心 !important

CSS 變數一起使用 !important 有點詭異,如下面的示範:

p {
  --color:red !important;

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

上面的 p 元素會是什麼顏色呢?你會認為是 red 紅色吧,認為按如下程式碼執行:

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

但是,並不是 red 紅色,因為實際執行是這樣:

p {
  color: red;
  color: blue;
}

在這種情況下,!important 並不是 color 的一部分,而是增加了 --color 變數的特性。規範中指明:

注意:自定義屬性可以包含 !important ,但是會被 CSS 解析器自動從屬性中刪除,這將自定義的屬性 important 變成層級。換言之, !important 並不是不會起作用,而是在語法檢查之前就被忽略了。

下面這個例子你會更容易明白:

p {
  --color: red !important;
  --color: blue;
  
  color: var(--color);
}

上面的程式碼會賦予 p 元素紅色,解析如下:

  • 我們對 --color 屬性有兩次的宣告,所以我們需要解決其層級的問題。第一次的定義帶有 !important ,所以它的層級相對高

  • 接著 var(--color) 將會應用 red !important

  • 所以我們會得到 color: red

再來看一段程式碼:

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

  color:var(--color);  color: pink; <!-- 這裡我改寫的顏色值 -->
}

按照上面的邏輯,我們最後會得到粉紅色 pink 的段落顏色。

一個很重要的規則是應當將 CSS 變數(自定義屬性)看作普通屬性,而不僅僅是儲存值的變數

自定義屬性是普通屬性,所以它們可以被定義在任何的元素上,可以使用普通屬性的繼承和聯級規則解決,可以使用 @media 和其他條件規則進行條件處理,可以用於 HTMLstyle 屬性,可使用 CSSDOM 讀取和設定等等。

2. 它們不可以儲存 urls

總有一天你會偶然發現這個常見的限制。

你不能這樣做 ❌

: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() 是怎麼解析的。解析起來有點棘手,推薦你到 Stack Overflow Answer 上找答案。正如你所見,我們修復起來很容易,把 url() 整個賦予變數即可。

3. 它們可以無效的值變有效

這也是我喜歡的點之一,也是讓人頭疼的點。

我們以一個基礎的案例入手:

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

.box 元素將會有一個紅藍漸變的效果,對吧?然而卻是純紅色。嗯,我打錯了 linear-*。我可以很容易發現這個錯誤,因為瀏覽器劃掉了這一行並啟用了上一行的背景樣式。

1.png

現在,讓我們來介紹變數:

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

測試這段程式碼,你會發現背景顏色變成了透明。我們的第二個背景顏色並沒有被瀏覽器劃掉,反而是第一個背景樣式被劃掉了。因為第二個背景樣式重寫了第一個。

2.png

為什麼會發生這樣的事情 ?

當我們使用變數作為屬性,瀏覽器只會在 "計算值時間" 去評估值,因為我們需要首先知道變數的內容。在這種例子中,當瀏覽器做聯級時,會認為屬性值是有效的,之後才會變成無效。

在我們的例子中,瀏覽器做級聯時,認為最後一個宣告是有效的。但是到評估值的時候,最後一個宣告被認定是無效的,所以它被忽略。我們不會回頭檢視,因為我們在級聯的時候已經處理過了,所以我們最後會得到一個透明的背景顏色。

你可能認為這種行為不符合邏輯的,但是它確符合邏輯。因為一個值是有效還是無效時基於 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() 函數被替代後,在「計算值時間」才做語法檢查。

簡單來說:CSS 變數將屬性的狀態作為一個後備,知道我們對其進行評估值。當評估值之後,我們可以說它是有效的或者無效的了。如果它是無效的,那麼久太晚了,因為我們不會再回頭使用上一個。

4. 它們可以不被使用單位

大多數的教材或課程都會展示下面的程式碼給你:

:root {
 --p: 10px;
}
.box {
  padding: var(--p);
}

但是,你也可以這麼做:

:root {
 --p: 10;
}
.box {
  padding: calc(var(--p)*1px);
}

變數中擁有單位並不是強制的。

5. 他們可以動起來

最初,CSS 變數被定義是沒有動畫屬性的。

Animatable: no

但是,事情發生了變化,我們通過 @property 修飾,CSS 變數可以做一些動畫或者漸變。這個特性目前瀏覽器支援比較低,但是也是時候瞭解下了。

6. 它們不可以儲存 inherit

我們考慮下面的例子:

<div class="box">
  <div class="item"></div>
</div>
.box {
  border:2px solid red;
}
.item {
  --b:inherit;
  border:var(--b);
}

直覺告訴我們,.item 將會即成父元素的 border,因為 --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" */
}

正如你所看到的,公共屬性應用,邏輯上才可以繼承 inherit

上面的寫法是雞肋的,因為 CSS 變數預設是繼承的。

7. 它們可以是空值

是的,你可以想下面這麼做:

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

筆記:<declatation-value> 宣告值必須代表一個標記,所以變數空值需要有一個空格。比如 --foo: ; 是有效的,var(--foo) 將會返回一個空格。--foo:; 是無效的。如下:

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

3.png

這個小技巧可以配合預設特性實現一些想不到的效果。

一個例子你就會明白這個技巧:

.box {
  background:
   linear-gradient(blue,transparent)
   var(--color,red); # 沒有發現--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>
  • 第一個 box 沒有定義變數,所以預設值被使用

  • 第二個有定義的變數,所以它被使用

  • 最後一個設定了一個空的變數值,所以空值被使用。使用後就好比不需要 var(--color, red) 一樣

空值允許我們移除屬性中 var() 宣告,在一個複雜的值中使用 var() 作用挺大。

8. CSS 變數不是 C++ 變數

很不幸的是,很多開發者趨向於比較 CSS 變數和其他語言的變數,然後在他們邏輯上有一大堆的問題。正是這個原因,我都不想叫他們變數而是自定義屬性,因為他們確實是屬性。

很多人都想這麼做:

:root {
  --p:5px;
  --p:calc(var(--p) + 1px); /* let's increment by 1px */
}
:root {
  --x: 5px;
  --y: 10px;
  /* let's do a variable switch */
  --c: var(--y);
  --y: var(--x);
  --x: var(--c);
}
.box {
  --s: 10px;
  margin:var(--s); /* I want 10px of margin */
  --s: 20px;
  padding:var(--s): /* then 20px of padding */
}

以上的示範都不能工作。第一個和第二個都有無效值,因為它們都有巢狀依賴。最後一個例子,paddingmargin 都是 20px,因為級聯會獲取第二個 --s: 20px 的變數去應用。

這很不幸,你應該停止用 C++, Javascript, Java 等語言的思維去思考 CSS 變數,因為它們有自己邏輯的自定義屬性。

9. 它們只能從父元素傳遞給子元素

請記住這個黃金規則:CSS 變數總是從父元素(或者說祖先元素)傳遞給子元素,不會從子元素傳遞給父元素或兄弟元素

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

你可以會認為 .box 背景顏色是 linear-gradient(green, blue) ? 不,背景顏色會是 linear-gradient(red, blue)

root 元素是 DOM 元素的最上層,所以它是 box 的祖先元素,我們應用上面的黃金規則知道,子元素的 --c1 是跑不到 linear-gradient(var(--c1),var(--c2) 中的。

10. 它們可以有奇怪的語法

最後一個也是有趣的一個。

你知道你能像下面這樣寫麼?

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

很神奇,對吧?是的,CSS 變數可以僅使用兩節虛線定義。

你會認為上面已經很瘋狂了?看下下面這個:

body {
 --?:red;
 --?:green; 
 --?:blue;
 --?:orange;
}

是的,你還可以用表情來定義一個變數。

CSS 變數允許你以 -- 開頭的任何內容。比如:

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

又比如:

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

其實上面是這樣的:

4.png

當然你不會使用這麼稀奇古怪的東西在你的實際專案中,除非你想讓你的老闆或合作開發者發瘋

總結

不知不覺有很多的內容了,你不需要馬上就記住所有的內容。我盡力將這些不為人知的 CSS 變數整理了出來。如果有一天你工作中需要這些知識點,你可以回頭來看。

英文原文地址:https://dev.to/afif/what-no-one-told-you-about-css-variables-553o

本文是譯文,採用意譯。

(學習視訊分享:)

以上就是關於 CSS 變數的一些你可能不瞭解的事!的詳細內容,更多請關注TW511.COM其它相關文章!