來讓網頁換換 SKIN 吧!一鍵切換你的網頁設計風格

00如是說
Starbugs Weekly 星巴哥技術專欄
7 min readJul 9, 2022
Photo by Taras Shypka on Unsplash

近期公司專案實作了切換主題功能,想想好像很少人會接觸到實際產品需要這種功能的,就決定來分享一下怎麼簡單實現這個功能,而且不只顏色,就連高度、寬度雜七雜八的都能換!

接下來就來一步一步完成這個聽起來很酷的東西!

基本概念

想要切換主題,在 CSS 這方面應該要怎麼做呢?第一個想到的應該就是 CSS 的屬性必須是「動態」的對吧!

而說到「動態」,很多人應該跟我一樣直接聯想到像是 Scss、Less 這種 CSS 預處理器,通過設定好一堆變數來達到切換屬性的效果,實際上也的確有很多 UI Components Framework 是這樣做的,讓你可以切換你的 Theme 來變換你的樣式。

但要注意一點,我們這可是要讓 Client 去做切換的,像剛剛提到的’預’處理器是在打包之前就要先決定好主題了,對於已經打包完成 Push 到 Production 的 Bundle 又要怎麼讓使用者去做切換呢?

這時候讓我們回歸初衷,回到最初的感動,其實原生的 CSS 就有支援 Variable 這個功能了!如下:

// CSS
:root {
--primary-color: green
}
.header {
height: 40px;
background: var(--primary-color);
}
// HTML
<header class="header"></header>

那如何做到動態切換呢?我們來寫一點 Javascript:

setTimeout(() => {
document.documentElement.style.setProperty('--primary-color', 'blue');
}, 3500)

完整程式碼如下,大家可以點進去看看效果:

所以接下來我們就來看看用 CSS 的 Variable 怎麼實作。

有人可能會 concern 瀏覽器的支援度如何?實際上以我們公司專案來說已經實際運行好一陣子了,目前為止都沒有收到 Feedback 是有關於該功能的問題,大部分瀏覽器都是可以正常運作的,而且就連著名的 TailwindCSS 也有使用哦!

所以如果不是特定要兼容一些比較冷門的瀏覽器或是較舊的版本,我想都是沒有問題的,但避免大家依舊會擔心,以下還是附上 Caniuse 給大家參考一下:
https://caniuse.com/css-variables

標準化樣式

既然要使用變數,就要把我們不同 Theme 要用的顏色規範好,我們先定好兩種就好,一個我們叫做 vanGogh,另一個我們就叫做 picasso 吧!

// vanGogh
--primary-color: green; // header 用
--secondary-color: red; // footer 用
// picasso
--primary-color: blue;
--secondary-color: pink;

實作切換按鈕

剛剛我們是直接執行 Javascript 去更換色系,二話不說就把它改掉了。但我們忽略了一點,就是使用者是上帝,沒有人使用的網站就沒有錢錢,沒有錢錢就沒有工程師了,所以我們應該把主動權交給他們,給他們按鈕去做切換:

這樣就實現簡單的切換主題功能了,但大家應該發現了,這樣一開始不就沒有顏色了嗎?所以我們再新增一個 setDefaultProperty 來給他初始值,初始值我們就用 vanGogh 吧:

變換高度

上述範例看似已經完成了,但這時偉大的 picaaso 不開心了,他覺得這個設計不夠抽象、不夠美麗,要調整一下頭腳的高度,這當然也是沒問題的!我們只要把原本寫死的屬性抽出來就可以了:

監聽變化

後來這個 picasso 的世紀創作被 vanGogh 看見了,他說他看到畫面被 picasso 改成這樣他差點吐血,沒辦法欣賞這樣的美,無奈的是,每個人都有自己的審美觀,我們不該強迫別人改變,於是他就說:「不然至少改變的時候通知一下吧!」,所以我們就來滿足他的需求。

MutationObserver

相信大家對這個東西應該很陌生,MutationObserver 這個 API 跟 Promise 在 Event loop 中一樣是屬於 Microtask 的,但平常真的不太會用到,所以很多人可能是第一次看到,這裡我們就使用它來完成我們的需求。

首先先來看一下用法:

我們知道我們要先 new MutationObserver 然後放一個 Callback 來做我們要做的事:

new MutationObserver(() => {
alert('變變變!!')
});

但這樣還沒有指定要監聽的對象,我們繼續往下看。

這裡我們看到了它提供了一些 Instance methods 可以使用,有一個叫做 observe 的 method 讓我們可以指定要監聽哪個 Node,以及設置一些相關的設定。

那我們這裡要監聽的就是我們的 document.documentElement,然後我們想知道他 style 的改變,於是我們的 options 就挑 attributes 來設定,如下:

const stylePropertyObserver = new MutationObserver(() => {
alert('變變變!!')
});
stylePropertyObserver.observe(document.documentElement, {
attributes: true,
attributeFilter: ['style'],
});

到這邊監聽的功能就算完成了,切換主題的功能也算是告一段落了!

想必有人也會好奇這個 API 還有什麼場景會使用到,這邊稍微提及一下 MutationObserver 的其他使用場景:

1. Vue 裡面的 nextTick :nextTick 的實作主要是使用 Promise,但如果瀏覽器不支援 Promise 的話,就會使用 MutationObserver 來做降級處理。

2. 文字編輯器的 highlight:舉例像是 # 後面的文字要做 highlight,就可以使用 MutationObserver 來偵測插入的文字來做相對應的處理,下面也提供一個可以參考的程式碼給大家看看
https://github.com/mitsuyacider/vue-hashtag-textarea

結語

這邊簡單介紹一下如何簡單一鍵切換網頁主題,如果我有說錯、說得不好的地方在麻煩大家留言告訴我哦!

參考資料

--

--