轉載請註明出處:葡萄城官網,葡萄城為開發者提供專業的開發工具、解決方案和服務,賦能開發者。
Sass和Less這樣的前處理器,讓我們的CSS程式碼保持良好的結構和可維護性。像變數、混合(mixins)、迴圈控制等特性,增強了動態編寫CSS的能力,從而減少重複程式碼,也加快了我們開發速度。
近年來,一些動態特性開始作為規範的一部分,出現在CSS語言中。CSS變數(CSS variables),或者用它的官方稱謂,叫作自定義屬性(custom properties),已經可用,並且有非常棒的瀏覽器支援,而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同樣是變數,儲存著number1與number2之和。在這裡它的值就是5。你可以動態地修改變數裡的值,並在程式中使用它們。在上面的程式碼中,我把number1的值更新為4,然後再進行求和。使用相同的變數,這個時候total裡儲存的值就是5,而不再是7了。
使用變數的妙處在於,它可以讓你在一個地方儲存值,並且讓你在後面能以各種理由去更新它。在程式中,你不需要為不同的值再新增額外的字元表示:任何值的更新都發生在同一個地方。正如,在你定義的變數上。
CSS在很大程度上是一種宣告式的語言,而缺少動態能力。你也許會認為,讓CSS擁有變數,幾乎讓上面的說法自相矛盾。如果前端開發僅僅是關於文字遊戲,那可以這麼說。幸運的是,Web的程式語言很像生活中的語言,它們會隨著周圍環境和實踐需要而不斷進化與適應。CSS也同樣如此。
簡單的說,變數已經成為CSS世界中激動人心的事實,並且你即將親自看到,對於這個厲害的新技術,學習和使用起來都非常直觀。
使用CSS變數有什麼好處?
使用CSS變數的好處,跟在其他程式語言中使用變數的好處沒什麼大的區別。
W3C 規範是這樣描述這一點的
使用CSS變數,給看似隨機的值加上富有資訊的名字,從而使得大檔案更容易閱讀和編輯,更少出錯。因為,你只需要在自定義屬性中改變一次值,所有應用了這個變數的地方都會自動跟著一起改變。
換句話說,通過給變數起一個對你來說在專案中有意義的名字,你能更容易的管理和維護你的程式碼。例如,當你為專案中的主色調設定一個變數名–primary-color,那麼你後面再修改這個主色調時,只需要改動一處,而不需要在不同位置的多個CSS檔案中去手動修改多次值。
CSS變數和前處理器中的變數有什麼不同?
你可能已經在CSS前處理器中嘗試過使用變數而帶來的好處了,比如Sass和Less。
前處理器讓你能設定變數,以及在函式、迴圈、數學計算等等地方中使用它們。這是否意味著CSS變數已經無關緊要了呢?
那可未必,主要是因為,CSS變數與前處理器中的變數其實是不同的東西。
這些不同基於一個事實:CSS變數是瀏覽器中直接可用的CSS屬性,而預處理中的變數是用於編譯成常規的CSS程式碼,瀏覽器其實對它們一無所知。
這意味著,你可以在樣式表中,在內聯樣式中,在SVG的標籤中直接更新CSS變數,甚至可以在執行時用JavaScript直接修改它。而你是無法對前處理器中的變數做上面這些操作的。CSS變數開啟了一個充滿可能性的新世界大門。
不是說你必須要在兩者間做出選擇:沒有什麼東西限制你,你可以同時使用CSS變數和預處理變數,並享有它們各自帶來的巨大好處。
CSS變數:語法
雖然本文為了簡潔,我使用了CSS變數(CSS variables)這個稱呼,但官方的規範把它們稱作作為級聯變數的CSS自定義屬性(CSS custom properties for cascading variables)。CSS自定義屬性(CSS custom property)的部分看起來像這樣:
--my-cool-background: #73a4f4;
在自定義屬性前新增雙橫線字首,然後像給普通CSS設值一樣,給自定義屬性設值。在上面的程式碼中,我給一個叫做–my-cool-backgroud的自定義屬性設定了一個顏色值。
而 級聯變數(cascading variable) 的部分,由通過val()來使用你的自定義屬性組成,開起來像這樣:
var(--my-cool-background);
自定義屬性作用於CSS選擇器中,val()可被當成一個真正的CSS屬性一樣使用。
:root {
--my-cool-background: #73a4f4;
}
/* CSS檔案的其他部分 */
#foo {
background-color: var(--my-cool-background);
}
上面的程式碼片段把–my-cool-background這個自定義屬性的作用域定義在:root這個偽類中,這讓該自定義屬效能被全域性訪問到(即在<html>標籤內部的任何地方)。然後,使用val()函式把ID為foo的容器的background-color設定為自定義屬性的值,這時該容器就有了淺藍的背景色。
這還沒完。你可以用同樣的淺藍色,給多個HTML標籤的多種可以設定顏色值的地方設值,比如設定它們的color和border-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變數中加入一個或多個備用值(fallback value/s),例如:
var(--main-color, #333);
上面的程式碼中,#333是一個備用值。當自定義屬性值無效或未指定(unset)時,如果這時也沒有指定備用值,那麼被繼承的(inherited)屬性值將會被使用。
CSS變數是區分大小寫的
與普通CSS屬性不同,CSS變數是區分大小寫的。
例如,var(–foo)和var(–FOO)是在求兩個不同的自定義屬性值,分別是–foo和–FOO的。
CSS變數受級聯關係影響
和普通CSS屬性一樣,CSS變數是可繼承的。例如,我們定義了一個屬性,值為blue:
:root {
--main-color: blue;
}
當你給任意在<html>標籤裡的元素指定–main-color變數時,它們都會繼承到blue這個值。
當你在另一個元素裡,為改自定義屬性設定了一個新值時,那麼所有該元素的子元素都會繼承那個新值。例如:
:root {
--main-color: blue;
}
.alert {
--main-color: red;
}
p {
color: var(--main-color);
}
<--! HTML -->
<html>
<head>
<!-- head code here -->
</head>
<body>
<div>
<p>blue 的段落</p>
<div class="alert">
<p>red 的段落</p>
</div>
</div>
</body>
</html>
在上面的標籤中,第一個p段落會繼承到全域性的–main-color值,它是藍色。
在div標籤中擁有.alert類的段落會是紅色,因為它的值繼承自區域性作用域裡的–main-color。
知道目前這些規則差不多夠了。讓我們開始寫程式碼吧!
如何在SVG中使用CSS變數
CSS變數和SVG配合得很好。你可以使用CSS變數去修改SVG中的樣式,以及和呈現相關的屬性。
舉個例子,假設你想讓你的SVG圖示能跟隨其所在父容器而擁有不同的顏色。你可以把CSS變數的作用域限定在父容器中,然後給變數設定想要的顏色,那麼裡面的圖示就會繼承父容器的顏色值。下面是相關程式碼:
/* inline SVG symbol for the icon */
<svg>
<symbol id="close-icon" viewbox="0 0 200 200">
<circle cx="96" cy="96" r="88" fill="none" stroke="var(--icon-color)" stroke-width="15" />
<text x="100" y="160" fill="var(--icon-color)" text-anchor="middle" style="font-size:250px;">x</text>
</symbol>
</svg>
/* first instance of the icon */
<svg>
<use xlink:href="#close-icon" />
</svg>
上面的程式碼使用了<symbol>標籤,它讓你建立一SVG圖形的不可見的版本。然後再使用<use>標籤生成一個可見的副本。這種方法可以讓你根據自己的喜好建立任意多個自定義的圖示,也就是通過它的ID(#close-icon)指向那個<symbol>。這比一遍又一遍地寫重複的程式碼建立圖形更加簡便。如果你想提高這方便的技術,Massimo Cassandro在他的Build Your Own SVG Icons中提供了一個快速教程。
注意到SVG symbol中,circle元素裡的stroke屬性和text元素裡的fill屬性:它們都使用了CSS變數,這裡是–icon-color。它被定義在:rootCSS檔案的選擇器中,像這樣:
:root {
--icon-color: black;
}
這是當前圖示看起來的樣子:
這時,如果你把同樣的SVG圖示放在不同的父容器中,並且在父容器上,給你的CSS變數設定各自的區域性值,那麼你就會得到不同顏色的圖示,並且不用給你的樣式表新增多餘的規則。這很酷!
為了展示這一點,我們把同樣的圖示放在一個有.success類的div裡:
<!-- html -->
<div class="success">
<svg>
<use xlink:href="#close-icon" />
</svg>
</div>
現在,讓–icon-color變數區域性化,即把它放在.success中,並設定一個green值。我們來看看發生的變化:
/* css */
.success {
--icon-color: green;
}
這個圖示的顏色就變成了綠色:
來看看一個完整的示例吧: 示例4程式碼。
如何在@keyframes中使用CSS變數
CSS變數可以在CSS動畫中使用,即可用於常規HTML元素,也可以用於內聯的SVG。只需要記得,你得知道讓什麼元素動,把它視為目標元素,然後建立對該目標元素的選擇器,在選擇器的作用範圍中定義你的CSS變數,然後,使用val()獲取這些變數,把它們設定到@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裡的屬性了。
這裡有個完整的例子供你體驗:示例5程式碼。
如何通過JavaScript操作CSS變數
另一個超級酷的事情就是,你可以直接通過JavaScript程式碼訪問CSS變數。
假設在你的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。
CSS變數的瀏覽器支援
除了IE11(它不支援CSS變數),所有主流瀏覽器都對CSS變數有全面地支援。
對於不支援CSS變數的瀏覽器,一個變通的方案是使用具有虛擬查詢條件(dummy conditional query)的@supports程式碼塊:
section {
color: gray;
}
@supports(--css: variables) {
section {
--my-color: blue;
color: var(--my-color, `blue`);
}
}
考慮到@supports在IE/Edge裡也起作用,上面的方法是可行的。如果你在val()函式中使用了備用值,那麼你的程式碼將更加可靠,它能在相容性不好的瀏覽器中實現優雅降級。
對於上面的程式碼,在Chrome和其他支援CSS變數的瀏覽器中,<section>標籤裡的文字將是藍色:
在IE11中,由於它不支援CSS變數,頁面將顯示灰色文字:
可以檢視線上的示例7程式碼。
此方法的一個缺點是,如果你大量使用CSS變數,而那些不支援CSS變數的瀏覽器在你的專案中有很高的適配優先順序,那麼相應的程式碼會變得很複雜,對於維護來說,甚至是噩夢。
在這種情況下,你可以選擇使用帶有cssnext的PostCSS,它能讓你在CSS程式碼中使用最新的特性,並且讓原本不支援這些屬性的瀏覽器,也能執行這些程式碼(有點像JavaScript轉換器做的事情)。
備註:這裡可下載本文所有示列程式碼。
本文是由葡萄城技術開發團隊釋出,轉載請註明出處:葡萄城官網
本中提到的CSS變數已經在SpreadJS和Wijmo中得到應用,