# Day 3:Playing with CSS Variables and JS [竹白記事本](https://chupainotebook.blogspot.com/),Javascript 30,紀錄。 ###### tags: `Javascript 30` ## 實現效果 使用 JavaScript 搭配原生 CSS 變數,製作出一個即時濾鏡效果工具。 可調整模糊、邊框色、內距大小。 - [原始碼](https://github.com/chupai/JS30/tree/master/source_code/Day03) - [原始狀態](https://chupai.github.io/JS30/source_code/Day03/index-START.html) - [範例效果](https://chupai.github.io/JS30/source_code/Day03/index-FINISHED.html) ## 重點 1. CSS Variables 2. 標題的 JS 顏色也要變化 3. 定義好得 `data-sizing` 用來判斷單位 ## 基礎語法 ### CSS - [CSS Variables](https://developer.mozilla.org/zh-CN/docs/Web/CSS/Using_CSS_custom_properties) - `filter:blur()` ### DOM - [`dataset`](https://developer.mozilla.org/zh-TW/docs/Web/API/HTMLElement/dataset) - [`setProperty()`](https://developer.mozilla.org/zh-CN/docs/Web/API/CSSStyleDeclaration/setProperty) - [`documentElement`](https://developer.mozilla.org/zh-TW/docs/Web/API/Document/documentElement) ## 說明 ### 1. CSS 必須先宣告全域的 CSS 變數,並套用到對應的頁面元素中(圖片、標題)。 ### 2. CSS Variables 支援度 `style` 所繼承的 `CSSStyleDeclaration` 介面,不支援 CSS Variables 的樣式屬性,所以必須使用 `setProperty()` 來設定樣式屬性。 ## 實作 ### 1. 步驟 #### Step 1 新增 CSS 變數 ```css :root { --base: #ffc600; --spacing: 10px; --blur: 10px; } img { padding: var(--spacing); background: var(--base); filter: blur(var(--blur)); } .hl { color: var(--base); } ```` `:root` 等同 `html` 選取器。 將資料變數放在 CSS 的優缺點: - 減少 JS 放資料的負擔 - 只修改一處 CSS 變數,所有套用此變數的樣式都會一起變動 - 寫程式時需要到 CSS 查看變數與修改 #### Step 2 對所有 `input` 標籤 註冊監聽事 ```javascript const input = const inputs = document.querySelectorAll('.controls input'); ``` 注意,`querySelectorAll` 出來的是 [`NodeList`](https://developer.mozilla.org/zh-TW/docs/Web/API/NodeList) 類陣列,不是陣列,陣列部分的功能它沒有。在 [Day 1](/ryWdlSIWH) 中有提到過。 ```javascript inputs.forEach(function(key) { key.addEventListener('change', changeHandler); }); ``` 為每個 `input` 標籤註冊監聽事件,當 `change` 事件發生,執行 `changeHandler` 函式。 由於 [`change`](https://developer.mozilla.org/zh-CN/docs/Web/Events/change) 事件並不即時,滑鼠放開才會執行,所以還需要再加入 [`mousemove`](https://developer.mozilla.org/zh-CN/docs/Web/API/Element/mousemove_event) 事件,使拉動滑桿時,也能更新效果。 ```javascript inputs.forEach(function(key) { key.addEventListener('change', changeHandler); key.addEventListener('mousemove', changeHandler); }); ``` #### Step 3 建立 `changeHandler` 函式 不使用 CSS 變數的作法: ```javascript function changeHandler() { const img = document.querySelector('img'); const hl = document.querySelector('.hl'); switch (this.name) { case 'spacing': img.style.padding = this.value + 'px'; break; case 'blur': img.style.filter = `blur(${this.value}px)`; break; case 'base': img.style.backgroundColor = this.value; hl.style.color = this.value; break; } } ``` 這樣的缺點是,當有兩處以上需要變動,就要手動增加。 依照題目使用 CSS 變數作法: ```javascript document.querySelector('html') document.querySelector('root') document.documentElement ``` 這三個是一樣的,依個人喜好選擇。 ```javascript function changeHandler() { const unit = this.dataset.sizing || ''; document.documentElement.style.setProperty( `--${this.name}`, this.value + unit ); } ``` 原始碼 HTML 有提供 `data-sizing` 屬性,是用來判斷要不要加上單位,因此這邊用 `dataset` 取得。 由於 `--變數` 是較新的 CSS 屬性 `style` 不論是 `.` 或 `[]` 都不支援,因此這裡用 `setProperty` 來加上 CSS 屬性。 這邊的 `this` 指向觸發 `changeHandler()` 的元素,因此可以獲取該元素的 `data-sizing`、`name`、`value` 值。 #### Step End ```javascript (function() { const inputs = document.querySelectorAll('.controls input'); function changeHandler() { const unit = this.dataset.sizing || ''; document.documentElement.style.setProperty( `--${this.name}`, this.value + unit ); } inputs.forEach(function(key) { key.addEventListener('change', changeHandler); key.addEventListener('mousemove', changeHandler); }); })(); ``` ### 2. 進節 #### 改用事件委派 與 Day1 相同,改用事件委派可以減少監聽器。 ```javascript const inputs = document.querySelector('.controls'); function changeHandler(e) { if (e.target.nodeName !== 'INPUT') { return; } const unit = e.target.dataset.sizing || ''; document.documentElement.style.setProperty( `--${e.target.name}`, e.target.value + unit ); } inputs.addEventListener('change', changeHandler); inputs.addEventListener('mousemove', changeHandler); ``` ### 3. 實作連結 - [初版](https://chupai.github.io/JS30/source_code/Day03/index1.html) - [使用事件委派](https://chupai.github.io/JS30/source_code/Day03/index.html)