這章的目的是學會如何宣告CSS變數, 在CSS內呼叫CSS變數, 以及用JS控制CSS變數.
下面有三條input range可以調整, 我們希望拖動spacing的range能夠改變畫面的padding大小, 拖動base可以同時改變標題中JS的字體顏色以及網頁的背景顏色, 拖動blur可以改變下方圖片的模糊度... 衝了!
<h2>Update CSS Variables with <span class='hl'>JS</span></h2>
<div class="controls">
<label for="spacing">Spacing:</label>
<input id="spacing" type="range" name="spacing" min="10" max="200" value="10" data-sizing="px">
<label for="blur">Blur:</label>
<input id="blur" type="range" name="blur" min="0" max="25" value="10" data-sizing="px">
<label for="base">Base Color</label>
<input id="base" type="color" name="base" value="#ffc600">
</div>
<img src="https://source.unsplash.com/7bwQXzbF6KE/800x500">
CSS變數的好處就是可以集中管理和重複使用! 透過實作讓字體和背景同時變色, 可以體悟到這點.
首先宣告CSS變數, CSS變數可以宣告在任何CSS Selector的範圍內, 宣告方式為--變數名稱: 變數值;
.
要使用宣告的變數, 用 var
函數將變數名稱包著, 像這樣 var(--base)
.
下面CSS樣式, .hl
的color
和img
的background
值都使用了宣告的變數--base
, 所以都會有#ffc600
這個顏色, 如果--base
儲存的顏色值被改變了, 標題的字體顏色和圖像的背景色都會跟著改變, 算是完成了第一步!
:root {
--base: #ffc600;
}
.hl {
color: var(--base);
}
img {
background: var(--base);
}
宣告及使用CSS變數, 和宣告及使用JS變數一樣, 是有作用範圍(scope)的.
若將變數宣告在某個元素的CSS內, 只有該元素本身以及該元素的子元素, 孫元素, 子子孫孫們能夠使用該變數.
有些人會將變數直接宣告在:root
這個虛擬類別(pseudo-class), 這個虛擬類別代表DOM的根, 也就是<html>
的CSS Selector, 由於它是整顆DOM的最高點, 也就是眾元素們的祖靈, 因此大家都可以輕易地使用掛在上面的CSS變數.
img
的padding
, filter
也用CSS變數串好, 像這樣.
:root {
--base: #ffc600;
--spacing: 10px;
--blur: 10px;
}
img {
padding: var(--spacing);
background: var(--base);
filter: blur(var(--blur));
}
.hl {
color: var(--base);
}
filter: blur(10px)
是CSS的濾鏡屬性, 可以讓被附加此屬性的元素有圖層濾鏡的效果, 像在網頁上用PS一樣. blur
是模糊濾鏡, 只是其中一種, 還有像contrast
反差, grayscale
灰階等的可以選.
接下來要讓input元素的調整range和CSS變數勾搭了. 藉此實踐拖曳range能夠同時改變圖片背景色和標題字體色的效果.
程式碼如下:
const inputs = document.querySelectorAll('.controls input');
inputs.forEach(input => input.addEventListener('change', handleUpdate));
inputs.forEach(input => input.addEventListener('mousemove', handleUpdate));
先選取全部的<input>
元素, 存到變數inputs
裡面.
為inputs
清單裡面存的每個<input>
元素都增設監聽器, 只要值有change
或是有滑鼠在上面mousemove
, 就用自訂的回呼函數handleUpdate
更新變動.
自訂函數要執行下列步驟:
<input>
目前的值.程式碼大致如下:
function handleUpdate() {
const suffix = this.dataset.sizing || '';
document.documentElement.style.setProperty(`--${this.name}`, this.value + suffix);
}
首先看到裡面用了很多this
, 這裡的this
會指向呼叫該函式的對象. 因為我們用forEach
分別為每個<input>
都設置了監聽器和回呼函式handleUpdate
, 因此函式內的this
會依照呼喚它的元素, 分別指向<input id="spacing">
, <input id="blur">
, <input id="base">
. 藉此得以取用該元素的屬性.
suffix
變數用來裝變數會用到的單位, 若遇到不需要CSS單位的變數, 就指定空值.
接著存取根元素內的變數, 改成對應<input>
目前指到的值, 後面加上單位.document.documentElement
為根元素<html>
, style.setProperty
用來設定CSS某個屬性. this
指到呼叫handleUpdate
的該<input>
.
整段跑起來像這樣:<input id="spacing>
的調整鈕被移動了! 裡面存的值從20變成40.
此時change
事件被觸發了! handleUpdate
函式執行!<input id="spacing">
有data-sizing="px"
, "px"
單位被存到變數suffix
裡面<input id=spacing">
具有事先設好的屬性name="base"
因為調整鈕被移動, 現在屬性value="40"
:root
內的CSS變數--base
的值被改成40px
.
希望這樣有比較清楚!
最後說一下為什麼要同時附加change
跟mousemove
的事件監聽器, 因為如果只有change
監聽器, 是監聽<input>
存的值被改變的瞬間觸發事件, 而值是在滑鼠放開的瞬間才會改變, 拖曳並不會放開滑鼠, 如果希望拖曳時值就要跟著改變, 就要多靠一個mousemove
.
以上就是JS30 第三篇心得!