无法接入Preline UI站点主题切换Select组件求助
用Select组件实现Preline UI主题切换的解决方案
之前使用Preline UI的按钮下拉组件可正常切换主题,但改用Select组件后无法生效,以下是适配后的完整实现代码:
一、修改后的Header组件代码(替换原主题切换Dropdown为Select)
import { usePathname } from "next/navigation"; import Link from "next/link"; import Navigation from "./Navigation"; import { useEffect, useState } from "react"; export default function TheHeader() { const [selectedTheme, setSelectedTheme] = useState("default"); useEffect(() => { // 从本地存储加载已保存的主题 const savedTheme = localStorage.getItem('hs_theme') || 'default'; setSelectedTheme(savedTheme); // 监听主题变化事件,同步Select选中状态 const syncThemeSelect = (e) => { setSelectedTheme(e.detail); }; window.addEventListener('on-hs-appearance-change', syncThemeSelect); return () => window.removeEventListener('on-hs-appearance-change', syncThemeSelect); }, []); // 处理Select选择变化 const handleThemeSelect = (e) => { const theme = e.target.value; window.HSThemeAppearance.setAppearance(theme); }; return ( <> <header className="select-none z-50 bg-white dark:bg-zinc-900 fixed w-full h-16 border-b border-solid border-zinc-300/80 dark:border-zinc-800/80"> <div className="px-20 h-full flex"> <div className="self-center flex flex-1"> <img src="/logo_header.png" alt="Logo" className="h-12" /> <div id="name-version"> <div className="text-lg lg:block ml-2 font-extrabold text-black dark:text-zinc-50">CodeCraft | Vanilla</div> <div className="lg:block ml-2 font-normal text-sm text-black dark:text-zinc-300 -mt-1">2 сезон</div> </div> </div> <Navigation /> <div className="flex flex-1 justify-end items-center gap-4"> {/* 主题切换Select组件 */} <div className="w-48"> <select value={selectedTheme} onChange={handleThemeSelect} className="w-full px-3 py-2 rounded-lg border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-800 text-sm text-zinc-900 dark:text-zinc-100 focus:outline-none focus:ring-2 focus:ring-blue-500" > <option value="auto">自动(跟随系统)</option> <option value="default">浅色模式</option> <option value="dark">深色模式</option> </select> </div> <Link href='/login' className='relative flex h-10 py-4 px-7 justify-center items-center rounded-2xl bg-blue-600 self-center text-zinc-50 text-center text-sm not-italic font-semibold hover:bg-blue-700' > Авторизоваться </Link> </div> </div> </header> <script src="/ChangeTheme.js" /> </> ); }
二、调整后的主题切换脚本(添加Select事件同步逻辑)
const HSThemeAppearance = { init() { const defaultTheme = 'default' let theme = localStorage.getItem('hs_theme') || defaultTheme if (document.querySelector('html').classList.contains('dark')) return this.setAppearance(theme) }, _resetStylesOnLoad() { const $resetStyles = document.createElement('style') $resetStyles.innerText = `*{transition: unset !important;}` $resetStyles.setAttribute('data-hs-appearance-onload-styles', '') document.head.appendChild($resetStyles) return $resetStyles }, setAppearance(theme, saveInStore = true, dispatchEvent = true) { const $resetStylesEl = this._resetStylesOnLoad() if (saveInStore) { localStorage.setItem('hs_theme', theme) } // 处理自动主题的实际生效值 let activeTheme = theme; if (theme === 'auto') { activeTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'default' } // 更新HTML的主题类 document.querySelector('html').classList.remove('dark', 'default') document.querySelector('html').classList.add(activeTheme) setTimeout(() => { $resetStylesEl.remove() }) if (dispatchEvent) { window.dispatchEvent(new CustomEvent('on-hs-appearance-change', {detail: theme})) } }, getAppearance() { let theme = this.getOriginalAppearance() if (theme === 'auto') { theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'default' } return theme }, getOriginalAppearance() { const defaultTheme = 'default' return localStorage.getItem('hs_theme') || defaultTheme } } HSThemeAppearance.init() // 监听系统主题变化,自动模式下同步更新 window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => { if (HSThemeAppearance.getOriginalAppearance() === 'auto') { HSThemeAppearance.setAppearance('auto', false) } }) window.addEventListener('load', () => { const $clickableThemes = document.querySelectorAll('[data-hs-theme-click-value]') const $switchableThemes = document.querySelectorAll('[data-hs-theme-switch]') const $themeSelects = document.querySelectorAll('select[onchange*="handleThemeSelect"]') // 保留原按钮切换逻辑 $clickableThemes.forEach($item => { $item.addEventListener('click', () => HSThemeAppearance.setAppearance($item.getAttribute('data-hs-theme-click-value'), true, $item)) }) // 保留原开关切换逻辑 $switchableThemes.forEach($item => { $item.addEventListener('change', (e) => { HSThemeAppearance.setAppearance(e.target.checked ? 'dark' : 'default') }) $item.checked = HSThemeAppearance.getAppearance() === 'dark' }) // 初始化Select选中值 $themeSelects.forEach($select => { $select.value = HSThemeAppearance.getOriginalAppearance() }) // 主题变化时同步所有控件状态 window.addEventListener('on-hs-appearance-change', e => { $switchableThemes.forEach($item => { $item.checked = e.detail === 'dark' }) $themeSelects.forEach($select => { $select.value = e.detail }) }) })
三、tailwind.config.js保持原配置不变
/** @type {import('tailwindcss').Config} */ module.exports = { darkMode: 'media', content: [ "./app/**/*.{js,ts,jsx,tsx,mdx}", "./pages/**/*.{js,ts,jsx,tsx,mdx}", "./components/**/*.{js,ts,jsx,tsx,mdx}", // Or if using `src` directory: "./src/**/*.{js,ts,jsx,tsx,mdx}", 'node_modules/preline/dist/*.js', ], theme: { extend: {}, }, plugins: [ require('tailwind-scrollbar'), require('preline/plugin'), ], }
关键修改说明
- 组件层:用原生Select替换Dropdown,通过React状态管理选中值,监听主题变化事件保持状态同步,选择变化时直接调用Preline的主题切换方法。
- 脚本层:添加Select元素的初始化和状态同步逻辑,修复原
setAppearance方法中主题类的设置逻辑,确保自动模式下正确应用系统主题。
内容的提问来源于stack exchange,提问作者zzhuravleff
相关产品推荐
相关产品推荐

