详解如何使用JavaScript构建主题切换器

 更新时间:2024年01月29日 09:54:26   作者:南城FE  
这篇文章主要为大家详细介绍了如何使用JavaScript构建一个主题切换器,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下

本文翻译自 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:这表示主题的选定模式,可能值为darklightsystem。它允许我们决定是否使用系统的主题。
  • systemTheme:它保存当前操作系统主题的值。即使我们选择了特定的主题(darklight),当操作系统主题发生变化时,我们仍会更新此变量,以确保用户切换到系统模式时,我们能正确调整主题。
  • theme:这是用户看到的实际主题,可能值为darklight
  • 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主题darklight
  • getThemeByMode():该函数根据我们的主题模式返回darklight。例如,如果模式设置为dark,则返回dark。但是,当模式设置为系统时,我们会检查系统主题,并根据操作系统的首选项,以darklight作为返回值。

这段代码不会出现在我们的createThemeStore()函数中。我们将window.matchMediaprefers-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主题切换器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 微信小程序的开发范式BeautyWe.js入门详解

    微信小程序的开发范式BeautyWe.js入门详解

    这篇文章主要介绍了微信小程序的开发范式BeautyWe.js详解,它是一套专注于微信小程序的企业级开发范式,它的愿景是:让企业级的微信小程序项目中的代码,更加简单、漂亮,需要的朋友可以参考下
    2019-07-07
  • 利用transition实现文字上下抖动的效果

    利用transition实现文字上下抖动的效果

    这篇文章主要给大家介绍了利用transition属性如何实现文字上下抖动的效果,文中给出了详细的介绍和完整的实例代码,相信对大家的学习具有一定的参考借鉴价值,有需要的朋友们下面来一起看看吧。
    2017-01-01
  • JS添加或删除HTML dom元素的方法实例分析

    JS添加或删除HTML dom元素的方法实例分析

    这篇文章主要介绍了JS添加或删除HTML dom元素的方法,结合实例形式分析了javascript针对HTML DOM元素节点的创建、追加、删除等相关操作技巧与注意事项,需要的朋友可以参考下
    2019-03-03
  • js实现简单模态窗口,背景灰显

    js实现简单模态窗口,背景灰显

    昨天中午做项目需要一个模态窗口,想起上一个公司的项目经理曾经做过一个比较牛的模态窗口,至今没用搞清楚实现原理,平时也没有时间去分析,试着自己做了一个,用了一天的时间终于完成了,给大家一起分享, 也希望高手多提意见。第一次在博客园上发文章,挺高兴的。
    2008-11-11
  • TypeScript如何开启严格空值检查

    TypeScript如何开启严格空值检查

    这篇文章主要介绍了TypeScript如何开启严格空值检查,文章围绕TypeScript的相关资料展开详情内容,需要的小伙伴可以参考一下
    2022-03-03
  • 防止浏览器记住用户名及密码的简单实用方法

    防止浏览器记住用户名及密码的简单实用方法

    很多浏览器都有自动填写功能,我在input上使用了autocomplete="off",但在有的浏览器上还是被记住了用户名跟密码,请问有没有更有效及简便的方法来防止浏览器记住用户名及密码
    2013-04-04
  • 详解Nuxt.js 实战集锦

    详解Nuxt.js 实战集锦

    这篇文章主要介绍了Nuxt.js 实战集锦,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-11-11
  • 选择复选框按钮置灰否则按钮可用

    选择复选框按钮置灰否则按钮可用

    这篇文章主要介绍了使用js实现选择复选框按钮置灰否则按钮可用,需要的朋友可以参考下
    2014-05-05
  • layui问题之模拟table表格中的选中按钮选中事件的方法

    layui问题之模拟table表格中的选中按钮选中事件的方法

    今天小编就为大家分享一篇layui问题之模拟table表格中的选中按钮选中事件的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-09-09
  • 基于BootStrap与jQuery.validate实现表单提交校验功能

    基于BootStrap与jQuery.validate实现表单提交校验功能

    学习前台后台最开始接触的业务都是用户注册和登录,下面通过本文给大家介绍BootStrap与jQuery.validate实现表单提交校验功能,感兴趣的朋友一起学习吧
    2016-12-12

最新评论