javascript技巧

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript技巧 > JavaScript主题切换器

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

作者:南城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个变量的状态:

添加订阅

当用户更改主题或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函数:

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

您可能感兴趣的文章:
阅读全文