1. 程式人生 > >CSS變數實用指南及注意事項

CSS變數實用指南及注意事項

近年來,一些動態特性已經開始成為 CSS 語言本身的一部分。 CSS變數 – 官方的術語為 “自定義屬性” – 已經已經加入規範並且具有很好的瀏覽器支援,而 CSS mixins 目前正在開發中 。

在本文中,你將瞭解如何開始將CSS變數整合到CSS開發工作流程中,讓你的樣式表更好維護,且減少重複。

讓我們一起深入瞭解吧!

什麼是CSS變數?

如果你使用過任何程式語言,那麼你已經熟悉了變數的概念。變數用於儲存和更新你的程式所需要的值,以便使它執行。

例如,請考慮以下JavaScript程式碼段:


let number1 = 2;
let number2 = 3;
let total = number1 + number2;
console.log(total); // 5
number1 = 4;
total = number1 + number2;
console.log(total); // 7

nubmer1 和 number2 是兩個變數,分別儲存著數字 2 和 3 。

total 同樣是變數,儲存著 number1number2 之和。在這裡它的值就是 5。你可以動態地修改變數裡的值,並在程式中使用它們。在上面的程式碼中,我把 number1 的值更新為 4,然後再進行求和。使用相同的變數,這個時候 total裡儲存的值就不再是 5 ,而是 7 了。

變數的好處在於你可以把值儲存在一個地方,然後在你需要的地方修改它。這樣你就不用在程式的不同地方為不同的值新增不同的變數:所有變數更新使用同一個儲存地址,比如你的變數。

CSS在很大程度上是一種宣告式的語言,缺乏動態性。你也許會認為,讓 CSS 擁有變數,似乎讓上面的說法自相矛盾。如果前端開發僅僅關注語義,那可以這麼說。幸運的是,Web的程式語言很像生活中的語言,它們會隨著周圍環境和實踐需求而不斷進化與適應。CSS也不例外。

總而言之,變數已經成為 CSS 中令人激動的實現,你很快也會發現,對於這個厲害的新技術,學習和使用起來都非常直觀。

使用CSS變數有什麼好處?

使用CSS變數的好處,跟在其他程式語言中使用變數的好處沒什麼大的區別。

以下是規範對此的說法:

[使用CSS變數]可以更容易地閱讀大檔案,因為看似任意的值,現在具有資訊性名稱,並且使此類檔案更容易編輯,且更不容易出錯,因為,你只需要在自定義屬性中改變一次值,所有應用了這個變數的地方都會自動跟著一起改變。W3C 規範

換句話說:

通過給變數起一個對你來說在專案中有意義的名字,你能更容易的管理和維護你的程式碼。例如,當你為專案中的主色調設定一個變數名--primary-color ,那麼你後面再修改這個主色調時,只需要改動一處,而不需要在不同位置的多個CSS檔案中去手動多次修改這個值。

CSS變數和前處理器中的變數有什麼不同?

你可能已經在CSS前處理器中嘗試過使用變數而帶來的好處了,比如 SassLess

前處理器讓你能設定變數,以及在函式、迴圈、數學計算等等地方中使用它們。這是否意味著CSS變數已經無關緊要了呢?

那可未必,主要是因為,CSS變數與前處理器中的變數其實並不是同樣的東西。

不同之處在於CSS變數是執行在瀏覽器中的動態CSS屬性,而前處理器變數會被編譯成普通的CSS程式碼。因此,瀏覽器並不知道前處理器變數的存在。

這意味著,你可以在樣式表,內聯樣式和SVG的標籤中直接更新CSS變數,或者使用JavaScript操作它們。這是前處理器變數做不到的。CSS變數提供了更多可能性!

但這並不是說你需要在二者之間選擇其一:沒有什麼東西限制你,你可以同時使用CSS變數和預處理變數,並享有它們各自帶來的巨大好處。

CSS變數:語法

雖然本文為了簡潔,我使用了CSS變數(CSS variables)這個術語,但是官方的規範把它們稱作為 級聯變數的CSS自定義屬性。CSS自定義屬性形式如下:


--my-cool-background: #73a4f4;

在自定義屬性前面新增雙橫線字首,然後像普通的CSS屬性一樣給它賦值。在上面的程式碼片段中,我給 --my-cool-background 自定義屬性賦了一個顏色值。

而 級聯變數(cascading variable) 的部分,由通過 var() 來使用你的自定義屬性,形式如下:


var(--my-cool-background);

自定義屬性的作用範圍限定在 CSS 選擇器中, var() 部分用作實際 CSS 屬性的值:


:root {
--my-cool-background: #73a4f4;
}
/* CSS檔案的其他部分 */
#foo {
background-color: var(--my-cool-background);
}

上面的程式碼片段把 --my-cool-background 自定義屬性的作用域定義在 :root 這個偽類中,這讓該自定義屬效能在全域性可用(它匹配<html>元素內的所有內容)。然後,使用 var() 函式把 ID 為 foo 的容器的 background-color 設定為自定義屬性的值,這時該容器就有了淺藍的背景色。

除此之外,還可以把淡藍色應用到多個HTML元素的其他顏色屬性上,如 colorborder-color 等。方法很簡單,就是通過 var(--my-cool-background) 獲取自定義屬性的值,然後給相應的CSS屬性設定上去。(當然,我建議在事情變得混亂之前考慮一下CSS變數的命名約定):


p {
color: var(--my-cool-background);
}

你也可以在CSS變數中使用另一個CSS變數,例如:


--top-color: orange;
--bottom-color: yellow;
--my-gradient: linear-gradient(var(--top-color), var(--bottom-color));

上面的程式碼建立了一個 --my-gradient 變數,是一個漸變樣式,它的值是使用 --top-color--bottom-color 變數建立的一個漸變。現在,你可以在任何地方通過僅僅改變變數的值來修改漸變,而不必到處在樣式表中建立漸變例項。

最後,你可以在CSS變數中加入一個或多個備用值,例如:


var(--main-color, #333);  

上面的程式碼中,#333是一個備用值。如果未設定備用值,則在自定義屬性無效或未設定的情況下,將應用繼承的值。

CSS變數是區分大小寫的

與普通CSS屬性不同,CSS變數是區分大小寫的。

例如,var(--foo)var(--FOO) 是獲取兩個不同的自定義屬性(分別是 --foo--FOO)的值。

CSS變數受級聯關係影響

和普通CSS屬性一樣,CSS變數是可繼承的。例如,我們定義了一個屬性,值為 blue :


:root {
--main-color: blue;
}

當你在 <html> 標籤中的任意元素指定 --main-color 變數時,它們都會繼承到blue這個值。

如果你在另一個元素裡面給自定義屬性賦了一個不同的值,這個元素的所有子元素就會繼承這個新值,例如:

CSS:


:root {
--main-color: blue;
}
.alert {
--main-color: red;
}
p {
color: var(--main-color);
}

HTML:


&lt;--! HTML --&gt;
&lt;html&gt;
&lt;head&gt;
&lt;!-- head code here --&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;div&gt;
&lt;p&gt;blue 的段落&lt;/p&gt;
&lt;div class="alert"&gt;
&lt;p&gt;red 的段落&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;

在上面的標籤中,第一個段落會繼承到全域性的 --main-color 值,它是藍色。

在div標籤中擁有 .alert 類的段落會是紅色,因為它的值繼承自區域性作用域裡的 --main-color ,這個變數的值是 red

內聯樣式中的 CSS 變數

CSS變數也可以在元素的內聯樣式中定義。 假設您有一個可以控制大小的元件,

你可以看到元素的內聯樣式中同樣可以定義CSS變數,而且同樣遵循相同的級聯規則。

var()函式

現在你知道了 var() 函式的用法。有關此功能的更多資訊。 看下面的程式碼,有一個紅色的 div 和一個綠色的div。只有一個CSS變數用於子綠色 div

現在從:root選擇器 刪除CSS變數 --background: green;,看看會發生什麼。

你可能猜測子元素將具有從父元素繼承的背景紅色。 錯了,這裡有點特殊情況。 當您在任何CSS屬性中使用該變數時,如果沒有定義變數,那麼它將預設採用預設值。 在這個例子中,背景顏色將是transparent(透明的):

你可能猜測子元素將具有從父元素繼承的背景紅色。 錯了,這裡有點特殊情況。 當您在任何CSS屬性中使用該變數時,如果沒有定義變數,那麼它將預設採用預設值。 在這個例子中,背景顏色將是transparent(透明的):

無效的值

如果CSS變數有一個無效的值,比如 --background: blah blah blah; 或拼寫錯誤 --background: yelow; /* yellow 拼寫錯誤 */,那麼 CSS 屬性將預設採用預設值,如:

background 預設值是 transparent
width 預設值是 auto
position 預設值是 static
opacity 預設值是 1
display 預設值是 inline

下面這個例子中,background的值為transparent,也就是說背景顏色是透明的。

回退值(fallback value)

有時可能會出現無法定義CSS變數的情況。在這種情況下,您可以將回退值設定為 var() 函式中的第二個引數

你也可以巢狀多個var() 函式 background: var(--color1, var(--color2, var(--color3, #00BCD4)));

結合 calc()函式

如果你以前從未使用過它,那麼我現在告訴你 calc() 函式是一個很實用的小工具,可以讓您執行計算以確定CSS值。 它在所有現代瀏覽器上都得到了很好的支援,並且可以與 CSS變數結合使用,以構建新值。 下面的例子中 <div>width是動態計算的,

結合媒體查詢 @media

你甚至可以在媒體查詢中重新設定變數值,並讓這些新值在任何地方使用它們級聯,特別要說明的是:這是前處理器變數無法實現的。

檢視此示例,其中媒體查詢更改用於設定非常簡單網格的變數,開啟 codepen,然後嘗試調整瀏覽器的大小,你可以看到媒體查詢中的 CSS 變數依然有效。

現在瞭解這些規則足夠,讓我們來編碼吧!

如何在SVG中使用CSS變數

CSS變數和SVG配合得很好。你可以使用CSS變數去修改SVG中的樣式,以及和呈現相關的屬性。

比如,你想通過SVG圖示元素的父元素來給它一個不同的顏色。你可以在父元素內設定一個區域性的CSS變數,然後把它賦值成你想要的顏色,然後,父元素內的圖示就能從父元素繼承到合適的顏色。

下面是相關程式碼:


/* 圖示的內聯SVG symbol */
&lt;svg&gt;
&lt;symbol id="close-icon" viewbox="0 0 200 200"&gt;
&lt;circle cx="96" cy="96" r="88" fill="none" stroke="var(--icon-color)" stroke-width="15" /&gt;
&lt;text x="100" y="160" fill="var(--icon-color)" text-anchor="middle" style="font-size:250px;"&gt;x&lt;/text&gt;
&lt;/symbol&gt;
&lt;/svg&gt;
/* 圖示的第一個例項  */
&lt;svg&gt;
&lt;use xlink:href="#close-icon" /&gt;
&lt;/svg&gt;

上面的程式碼使用了 <symbol> 標籤,它讓你建立一 SVG 圖形的不可見的版本。然後再使用 <use> 標籤生成一個可見的副本。這種方法可以讓你根據自己的喜好建立任意多個自定義的圖示,也就是通過它的ID( #close-icon )指向那個 <symbol> 。這比一遍又一遍地寫重複的程式碼建立圖形更加簡便。如果你想提高這方便的技術,Massimo Cassandro在他的 創造你自己的SVG圖示 中提供了一個快速教程。

注意 SVG中的圓形元素的 stroke 屬性值和文字元素的 fill 屬性值:它們都使用了一個CSS變數,--icon-color ,這個變數定義在CSS文件的 :root 選擇器上,如下所示: 


:root {
--icon-color: black;
}

這是當前圖示看起來的樣子:

如果你現在把SVG圖示放到不同的容器中,然後在每個父元素的選擇器中給這個變數賦不同的顏色值,你就能在不新增任何樣式規則的情況下建立不同顏色的圖示。這很酷!

為了展示這一點,我們把上面圖示的一個例項放在一個有 .success 類的 div 中。

HTML 程式碼:


&lt;!-- html --&gt;
&lt;div class="success"&gt;
&lt;svg&gt;
&lt;use xlink:href="#close-icon" /&gt;
&lt;/svg&gt;
&lt;/div&gt;

現在,讓 --icon-color 變數區域性化,即把它放在 .success 中,並設定一個 green 值。我們來看看發生的變化:

CSS 程式碼:


/* css */
.success {
--icon-color: green;
}

這個圖示的顏色就變成了綠色:

如何在@keyframes中使用CSS變數

CSS變數可以在CSS動畫中使用,不論是在一般的HTML元素還是內聯SVG元素上。只需要記得,你得知道讓什麼元素動,把它視為目標元素,然後建立對該目標元素的選擇器,在選擇器的作用範圍中定義你的CSS變數,然後,使 var() 獲取這些變數,把它們設定到 @keyframes 程式碼塊中。

例如,讓SVG中 .bubble 類裡面的 <ellipse> 元素動起來,你的CSS可能會看起來像這樣:


.bubble {
--direction-y: 30px;
--transparency: 0;
animation: bubbling 3s forwards infinite;
}
@keyframes bubbling {
0% {
transform: translatey(var(--direction-y));
opacity: var(--transparency);
}
40% {
opacity: calc(var(--transparency) + 0.2);
}
70% {
opacity: calc(var(--transparency) + 0.1);
}
100% {
opacity: var(--transparency);
}
}

注意到這是如何藉助 CSS的 calc() ,並用 var() 函式進行計算的。它們增強了你程式碼的靈活性。

這個例子簡潔的地方在於,利用CSS屬性,你可以簡單的修改相應選擇器裡變數值而調整動畫,而不需要挨個去查詢 @keyframes 裡的屬性了。

如何通過JavaScript操作CSS變數

另一個超級酷的事情就是,你可以直接通過JavaScript程式碼訪問CSS變數。通過 getComputedStylesetPropertygetPropertyValue從JavaScript訪問CSS變數非常簡單。 要獲取變數,請使用 getPropertyValue()

假設在你的CSS檔案中,有一個叫做 --left-pos的變數,作用在 .sidebar 選擇器中,值為 100px:


.sidebar {
--left-pos: 100px;
}

那麼,使用類似下面的 JavaScript 程式碼獲取 --left-pos 的值:


// 快取你即將操縱的元素
const sidebarElement = document.querySelector('.sidebar');
// 快取sidebarElement的樣式於cssStyles中
const cssStyles = getComputedStyle(sidebarElement);
// 獲取 --left-pos CSS變數的值
const cssVal = String(cssStyles.getPropertyValue('--left-pos')).trim();
// 將CSS 變數的值列印到控制檯: 100px
console.log(cssVal);

使用類似下面的JavaScript程式碼給CSS變數賦值:


sidebarElement.style.setProperty('--left-pos', '200px');

上面的程式碼將sidebar元素中 --left-pos 變數的值設定為 200px

請看看CodePen中的如下示例,你可以互動式地點選側邊欄,修改 blend mode 屬性和背景色。這些實現只用到了CSS變數和JavaScript。

還有一些簡單的方法,這裡來看看不使用 getComputedStyle(),獲取變數值:

JavaScript 程式碼:


/* 從 :root 根元素獲取變數值 */
document.documentElement.style.getPropertyValue('--background');
/* 從 .block-3 元素獲取變數值 */
document.querySelector('.block-3').style.getPropertyValue('--background');

修改變數值:


/* 修改 :root 根元素的變數值 */
document.documentElement.style.setProperty('--background', '#ff0000');
/* 修改 .block-3 元素的變數值 */
document.querySelector('.block-3').style.setProperty('--background', '#ff0000');

其他一些注意點
還有一些有趣的事情,在開發時候需要注意。

空值和空格


/* 無效的 */
--color:;
/* 有效的 */
--color: ; /* 值是空格 */

背景圖片 url()


/* 無效的 - CSS 不支援拼接*/
.logo{
--logo-url: 'logo';
background: url('assets/img/' var(--logo-url) '.png');
}
/* 無效的 - CSS bug */
.logo{
--logo-url: 'assets/img/logo.png';
background: url(var(--logo-url));
}
/* 有效的 */
.logo{
--logo-url: url('assets/img/logo.png');
background: var(--logo-url);
}

單位相關


/* 無效的 */
--width: 10;
width: var(--width)px;
/* 有效的 */
--width: 10px;
width: var(--width);
/* 有效的 */
--width: 10;
width: calc(1px * var(--width)); /* 乘以1個單位進行轉換 */
width: calc(1em * var(--width));

瀏覽器對CSS變數的支援情況

除了IE11(它不支援CSS變數),所有主流瀏覽器都對CSS變數有全面地支援。

對於不支援CSS變數的瀏覽器,一個變通的方案是使用具有虛擬查詢條件(dummy conditional query)的 @supports程式碼塊:


section {
color: gray;
}
@supports(--css: variables) {
section {
--my-color: blue;
color: var(--my-color, 'blue');
}
}

因為IE/Edge支援 @supports ,所以上面的程式碼會生效。如果在var()函式中新增一個後備值,你的程式碼將會更加健壯,在支援的更加不好的瀏覽器中也能優雅降級。

所以,在Chrome和其他支援CSS變數的瀏覽器中,<section>元素內部的文字是藍色的:

在IE11中,由於它不支援CSS變數,頁面將顯示灰色文字:

這種方式的缺點是如果你在專案中使用了大量的CSS變數,但是該專案主要通過不支援CSS變數的瀏覽器開啟,那麼程式碼不僅會變得有點兒複雜,維護也將會是噩夢。

在這種情況下,你可以選擇使用支援 cssnext 的PostCSS,然後你就可以編寫尖端的CSS程式碼了,相容不支援的瀏覽器交給PostCSS去做就可以了,這有點兒像JavaScript的編譯器。

這裡推薦一下我的前端學習交流群:784783012,裡面都是學習前端的,如果你想製作酷炫的網頁,想學習程式設計。自己整理了一份2018最全面前端學習資料,從最基礎的HTML+CSS+JS【炫酷特效,遊戲,外掛封裝,設計模式】到移動端HTML5的專案實戰的學習資料都有整理

點選:加入