詳解如何使用JavaScript構(gòu)建主題切換器
本文翻譯自 How to implement Theme Switcher in JavaScript,作者:Pavel Keyzik, 略有刪改。

在本文中,您將學(xué)習(xí)如何在JavaScript中構(gòu)建主題切換器。這應(yīng)該是一件很容易的事情,但你也可以從我的代碼中學(xué)到一些東西。
我們需要處理哪些場(chǎng)景
- 我們應(yīng)該解決的最基本的場(chǎng)景之一是將主題從亮到暗,反之亦然。
- 我們需要解決的第二件事是,有些人更喜歡使用與系統(tǒng)中相同的設(shè)置。這對(duì)那些整天在黑暗和光明主題之間切換的人很有用。
- 第三件事是保存用戶首選項(xiàng),否則刷新頁面后所有設(shè)置將再次設(shè)置為默認(rèn)值。
創(chuàng)建主題存儲(chǔ)
我們創(chuàng)建初始函數(shù)createThemeStore(),它將包含幾乎所有內(nèi)容。我們?cè)谶@里用這種方式實(shí)現(xiàn),但這可能不是最佳方法。
function createThemeStore(options) {
// Initial mode
const initialMode = options.defaultMode || 'system'
// Initial state
const state = {
mode: initialMode,
systemTheme: getSystemTheme(),
theme: getThemeByMode(initialMode),
}
}在這里,我們創(chuàng)建一個(gè)只有3個(gè)變量的狀態(tài):
mode:這表示主題的選定模式,可能值為dark、light或system。它允許我們決定是否使用系統(tǒng)的主題。systemTheme:它保存當(dāng)前操作系統(tǒng)主題的值。即使我們選擇了特定的主題(dark或light),當(dāng)操作系統(tǒng)主題發(fā)生變化時(shí),我們?nèi)詴?huì)更新此變量,以確保用戶切換到系統(tǒng)模式時(shí),我們能正確調(diào)整主題。theme:這是用戶看到的實(shí)際主題,可能值為dark或light。options.defaultMode:用于恢復(fù)正確的主題首選項(xiàng)。例如,您可以在localStorage中保存主題更改,然后將其用作默認(rèn)值,確保保留用戶的首選項(xiàng)。
添加訂閱
當(dāng)用戶更改主題或OS主題更新時(shí),我們需要一種方法來通知我們的代碼,這就是使用訂閱的地方,我們需要允許訂閱state對(duì)象中的更改。下面的代碼將幫助我們完成它,記住現(xiàn)在我們?cè)?code>createThemeStore()中執(zhí)行所有操作。
function createThemeStore(options) {
// ...
// Create subscriptions object to be able notify subscribers
const subscriptions = new Map()
let subscriptionId = 0 // Just a unique id for every subscriber
// A place where we send notification to all of our subscribers
function notifyAboutThemeChange(theme) {
subscriptions.forEach((notify) => {
const notificationData = {
mode: state.mode,
theme,
}
notify(notificationData) // Calls subscribed function (The example how we use it will be later)
})
}
// A function that allows to subscribe to state changes
function subscribe(callback) {
subscriptionId++
subscriptions.set(subscriptionId, callback)
state.systemTheme = getSystemTheme() // We'll define it later
if (state.mode === 'system') {
notifyAboutThemeChange(state.systemTheme)
} else {
notifyAboutThemeChange(state.theme)
}
return subscriptionId
}
// A function that allows to unsubscribe from changes
function usubscribe(subscriptionId) {
subscriptions.delete(subscriptionId)
}
return {
subscribe,
usubscribe,
}
}使用方式:
// Create a theme store
const store = createThemeStore()
// Suscribe to changes
const subscriptionId = store.subscribe((newTheme) => {
// Here you'll be seeing theme changes
console.log(newTheme)
})
// When you need to unsubscribe from theme change, you just call
store.usubscribe(subscriptionId)檢測(cè)系統(tǒng)主題首選項(xiàng)
現(xiàn)在我們有了基本的代碼結(jié)構(gòu),讓我們?cè)俣x兩個(gè)helper函數(shù):
getSystemTheme():該函數(shù)返回當(dāng)前OS主題dark或lightgetThemeByMode():該函數(shù)根據(jù)我們的主題模式返回dark或light。例如,如果模式設(shè)置為dark,則返回dark。但是,當(dāng)模式設(shè)置為系統(tǒng)時(shí),我們會(huì)檢查系統(tǒng)主題,并根據(jù)操作系統(tǒng)的首選項(xiàng),以dark或light作為返回值。
這段代碼不會(huì)出現(xiàn)在我們的createThemeStore()函數(shù)中。我們將window.matchMedia與prefers-color-scheme媒體查詢一起使用來確認(rèn)當(dāng)前系統(tǒng)的主題值。
const mediaQuery = '(prefers-color-scheme: dark)'
// Get's current OS system
function getSystemTheme() {
if (window.matchMedia(mediaQuery).matches) {
return 'dark'
}
return 'light'
}
// Based on user's preferences we return correct theme
function getThemeByMode(mode) {
if (mode === 'system') {
return getSystemTheme()
}
return mode
}
function createThemeStore(options) {
// ...
}現(xiàn)在我們唯一需要做的就是添加事件監(jiān)聽器,以檢測(cè)操作系統(tǒng)主題的變化。
function createThemeStore(options) {
// ...
// When the OS preference has changed
window.matchMedia(mediaQuery).addEventListener('change', (event) => {
const prefersDarkMode = event.matches
// We change system theme
state.systemTheme = prefersDarkMode ? 'dark' : 'light'
// And if user chose `system` mode we notify about the change
// in order to be able switch theme when OS settings has changed
if (state.mode === 'system') {
notifyAboutThemeChange(state.systemTheme)
}
})
}添加手動(dòng)更改主題模式的功能
現(xiàn)在每當(dāng)我們的操作系統(tǒng)首選項(xiàng)更改時(shí),我們已經(jīng)實(shí)現(xiàn)了主題的自動(dòng)更新。我們還沒有討論的是主題模式的手動(dòng)更新。你將在你的深色、淺色和系統(tǒng)主題按鈕上使用這個(gè)功能。
function createThemeStore(options) {
// ...
function changeThemeMode(mode) {
const newTheme = getThemeByMode(mode)
state.mode = mode
state.theme = newTheme
if (state.mode === 'system') {
// If the mode is system, send user a system theme
notifyAboutThemeChange(state.systemTheme)
} else {
// Otherwise use the one that we've selected
notifyAboutThemeChange(state.theme)
}
}
return {
subscribe,
usubscribe,
changeThemeMode,
}
}使用示例
我們的代碼是純JavaScript實(shí)現(xiàn),你可以在任何地方使用它。我將在React中演示一個(gè)示例,但您可以在任何您喜歡的框架或庫中嘗試它。
// Create a theme store from saved theme mode
// or use `system` if user hasn't changed preferences
const store = createThemeStore({
defaultMode: localStorage.getItem("theme") || "system",
});
function MyComponent() {
// Initial active theme is `null` here, but you could use the actual value
const [activeTheme, setActiveTheme] = useState(null)
useEffect(() => {
// Subscribe to our store changes
const subscriptionId = store.subscribe((notification) => {
// Update theme
setActiveTheme(notification.theme)
// Save selected theme mode to localStorage
localStorage.setItem('theme', notification.mode)
})
return () => {
store.usubscribe(subscriptionId)
}
}, [])
return (
<>
<p>
Active theme: <b>{activeTheme}</b>
</p>
<p>Change theme to:</p>
<button onClick={() => store.changeThemeMode("dark")}>Dark</button>
<button onClick={() => store.changeThemeMode("light")}>Light</button>
<button onClick={() => store.changeThemeMode("system")}>System</button>
<>
)
}最后
本文介紹了如何在JavaScript中實(shí)現(xiàn)主題切換。通過創(chuàng)建一個(gè)主題存儲(chǔ)器以及添加訂閱功能以便在主題發(fā)生變化時(shí)通知調(diào)用方,最后還增加了手動(dòng)切換主題模式和在React中演示的代碼示例。
如果你的網(wǎng)站正有主題切換的需求,不妨可以試試看。
到此這篇關(guān)于詳解如何使用JavaScript構(gòu)建主題切換器的文章就介紹到這了,更多相關(guān)JavaScript主題切換器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Javascript拖拽系列文章1之offsetParent屬性
這個(gè)系列文章主要是講述實(shí)現(xiàn)Javascript拖拽功能的基礎(chǔ)知識(shí),并將在最后給出一個(gè)完整的示例。適合對(duì)拖拽完全不懂的人閱讀2008-09-09
全面解析Bootstrap彈窗的實(shí)現(xiàn)方法
這篇文章全面解析Bootstrap彈窗的實(shí)現(xiàn)方法,對(duì)其結(jié)構(gòu)進(jìn)行詳細(xì)分析,感興趣的小伙伴們可以參考一下2015-12-12
JavaScript導(dǎo)航腳本判斷當(dāng)前導(dǎo)航
這篇文章主要介紹了JavaScript導(dǎo)航腳本判斷當(dāng)前導(dǎo)航的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-07-07
JS中把函數(shù)作為另一函數(shù)的參數(shù)傳遞方法(總結(jié))
下面小編就為大家?guī)硪黄狫S中把函數(shù)作為另一函數(shù)的參數(shù)傳遞方法(總結(jié))。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-06-06
中國地區(qū)三級(jí)聯(lián)動(dòng)下拉菜單效果分析
主要的數(shù)據(jù)和功能實(shí)現(xiàn)都是在js文件中,網(wǎng)上找的地區(qū)數(shù)據(jù)有的地方不完整,需要自己添加,本文將詳細(xì)介紹2012-11-11

