詳解如何使用JavaScript構(gòu)建主題切換器
本文翻譯自 How to implement Theme Switcher in JavaScript,作者:Pavel Keyzik, 略有刪改。
在本文中,您將學習如何在JavaScript
中構(gòu)建主題切換器。這應該是一件很容易的事情,但你也可以從我的代碼中學到一些東西。
我們需要處理哪些場景
- 我們應該解決的最基本的場景之一是將主題從亮到暗,反之亦然。
- 我們需要解決的第二件事是,有些人更喜歡使用與系統(tǒng)中相同的設(shè)置。這對那些整天在黑暗和光明主題之間切換的人很有用。
- 第三件事是保存用戶首選項,否則刷新頁面后所有設(shè)置將再次設(shè)置為默認值。
創(chuàng)建主題存儲
我們創(chuàng)建初始函數(shù)createThemeStore()
,它將包含幾乎所有內(nèi)容。我們在這里用這種方式實現(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)建一個只有3個變量的狀態(tài):
mode
:這表示主題的選定模式,可能值為dark
、light
或system
。它允許我們決定是否使用系統(tǒng)的主題。systemTheme
:它保存當前操作系統(tǒng)主題的值。即使我們選擇了特定的主題(dark
或light
),當操作系統(tǒng)主題發(fā)生變化時,我們?nèi)詴麓俗兞浚源_保用戶切換到系統(tǒng)模式時,我們能正確調(diào)整主題。theme
:這是用戶看到的實際主題,可能值為dark
或light
。options.defaultMode
:用于恢復正確的主題首選項。例如,您可以在localStorage
中保存主題更改,然后將其用作默認值,確保保留用戶的首選項。
添加訂閱
當用戶更改主題或OS主題更新時,我們需要一種方法來通知我們的代碼,這就是使用訂閱的地方,我們需要允許訂閱state
對象中的更改。下面的代碼將幫助我們完成它,記住現(xiàn)在我們在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)
檢測系統(tǒng)主題首選項
現(xiàn)在我們有了基本的代碼結(jié)構(gòu),讓我們再定義兩個helper
函數(shù):
getSystemTheme()
:該函數(shù)返回當前OS主題dark
或light
getThemeByMode()
:該函數(shù)根據(jù)我們的主題模式返回dark
或light
。例如,如果模式設(shè)置為dark
,則返回dark
。但是,當模式設(shè)置為系統(tǒng)時,我們會檢查系統(tǒng)主題,并根據(jù)操作系統(tǒng)的首選項,以dark
或light
作為返回值。
這段代碼不會出現(xiàn)在我們的createThemeStore()
函數(shù)中。我們將window.matchMedia
與prefers-color-scheme
媒體查詢一起使用來確認當前系統(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)聽器,以檢測操作系統(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) } }) }
添加手動更改主題模式的功能
現(xiàn)在每當我們的操作系統(tǒng)首選項更改時,我們已經(jīng)實現(xiàn)了主題的自動更新。我們還沒有討論的是主題模式的手動更新。你將在你的深色、淺色和系統(tǒng)主題按鈕上使用這個功能。
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
實現(xiàn),你可以在任何地方使用它。我將在React
中演示一個示例,但您可以在任何您喜歡的框架或庫中嘗試它。
// 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中實現(xiàn)主題切換。通過創(chuàng)建一個主題存儲器以及添加訂閱功能以便在主題發(fā)生變化時通知調(diào)用方,最后還增加了手動切換主題模式和在React
中演示的代碼示例。
如果你的網(wǎng)站正有主題切換的需求,不妨可以試試看。
到此這篇關(guān)于詳解如何使用JavaScript構(gòu)建主題切換器的文章就介紹到這了,更多相關(guān)JavaScript主題切換器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Javascript拖拽系列文章1之offsetParent屬性
這個系列文章主要是講述實現(xiàn)Javascript拖拽功能的基礎(chǔ)知識,并將在最后給出一個完整的示例。適合對拖拽完全不懂的人閱讀2008-09-09JS中把函數(shù)作為另一函數(shù)的參數(shù)傳遞方法(總結(jié))
下面小編就為大家?guī)硪黄狫S中把函數(shù)作為另一函數(shù)的參數(shù)傳遞方法(總結(jié))。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-06-06