详解如何使用JavaScript构建主题切换器
作者:南城FE
本文翻译自 How to implement Theme Switcher in JavaScript,作者:Pavel Keyzik, 略有删改。
在本文中,您将学习如何在JavaScript
中构建主题切换器。这应该是一件很容易的事情,但你也可以从我的代码中学到一些东西。
我们需要处理哪些场景
- 我们应该解决的最基本的场景之一是将主题从亮到暗,反之亦然。
- 我们需要解决的第二件事是,有些人更喜欢使用与系统中相同的设置。这对那些整天在黑暗和光明主题之间切换的人很有用。
- 第三件事是保存用户首选项,否则刷新页面后所有设置将再次设置为默认值。
创建主题存储
我们创建初始函数createThemeStore()
,它将包含几乎所有内容。我们在这里用这种方式实现,但这可能不是最佳方法。
function createThemeStore(options) { // Initial mode const initialMode = options.defaultMode || 'system' // Initial state const state = { mode: initialMode, systemTheme: getSystemTheme(), theme: getThemeByMode(initialMode), } }
在这里,我们创建一个只有3个变量的状态:
mode
:这表示主题的选定模式,可能值为dark
、light
或system
。它允许我们决定是否使用系统的主题。systemTheme
:它保存当前操作系统主题的值。即使我们选择了特定的主题(dark
或light
),当操作系统主题发生变化时,我们仍会更新此变量,以确保用户切换到系统模式时,我们能正确调整主题。theme
:这是用户看到的实际主题,可能值为dark
或light
。options.defaultMode
:用于恢复正确的主题首选项。例如,您可以在localStorage
中保存主题更改,然后将其用作默认值,确保保留用户的首选项。
添加订阅
当用户更改主题或OS主题更新时,我们需要一种方法来通知我们的代码,这就是使用订阅的地方,我们需要允许订阅state
对象中的更改。下面的代码将帮助我们完成它,记住现在我们在createThemeStore()
中执行所有操作。
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)
检测系统主题首选项
现在我们有了基本的代码结构,让我们再定义两个helper
函数:
getSystemTheme()
:该函数返回当前OS主题dark
或light
getThemeByMode()
:该函数根据我们的主题模式返回dark
或light
。例如,如果模式设置为dark
,则返回dark
。但是,当模式设置为系统时,我们会检查系统主题,并根据操作系统的首选项,以dark
或light
作为返回值。
这段代码不会出现在我们的createThemeStore()
函数中。我们将window.matchMedia
与prefers-color-scheme
媒体查询一起使用来确认当前系统的主题值。
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) { // ... }
现在我们唯一需要做的就是添加事件监听器,以检测操作系统主题的变化。
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) } }) }
添加手动更改主题模式的功能
现在每当我们的操作系统首选项更改时,我们已经实现了主题的自动更新。我们还没有讨论的是主题模式的手动更新。你将在你的深色、浅色和系统主题按钮上使用这个功能。
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
实现,你可以在任何地方使用它。我将在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中实现主题切换。通过创建一个主题存储器以及添加订阅功能以便在主题发生变化时通知调用方,最后还增加了手动切换主题模式和在React
中演示的代码示例。
如果你的网站正有主题切换的需求,不妨可以试试看。
到此这篇关于详解如何使用JavaScript构建主题切换器的文章就介绍到这了,更多相关JavaScript主题切换器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!