使用JavaScript模块切换主题时默认样式闪烁问题求助
解决主题切换时的页面闪烁问题
问题描述
我实现了一个基于localStorage的主题切换功能,通过给<html>标签添加lightMode/darkMode类来切换CSS变量,但刷新页面时,默认的浅色主题会先闪烁一下,才会切换到用户设置的主题。
我的CSS代码:
:root.lightMode { --text: #030202; --background: #f0efef; } :root.darkMode { --text: #f9f4f4; --background: #100f0f; } :root { --text: #030202; --background: #f0efef; }
原JavaScript模块代码:
export let colorMode = "auto", localStorageAvailable = false; export function updateColorMode(useLocalStorage, colorModePreference) { /*useLocalStorage = true | false; colorModePreference = null | "auto" | "light" | "dark";*/ //Removes color mode classes from root then adds the desired one: function switchRootClass(className) { const rootClassList = document.documentElement.classList; if (rootClassList.contains("lightMode")) { rootClassList.remove("lightMode"); }; if (rootClassList.contains("darkMode")) { rootClassList.remove("darkMode"); }; rootClassList.add(className); }; //Check if localStorage is available: function checkLocalStorage() { try { localStorage.setItem("test", "test"); localStorage.removeItem("test"); return true; } catch (error) { return false; } }; if (useLocalStorage === true && checkLocalStorage()) { localStorageAvailable = true; //Check if localStorage item exist: if (!localStorage.getItem("colorMode")) { localStorage.setItem("colorMode", "auto"); }; } else { localStorageAvailable = false; }; //Update colorMode to localStorage item or custom value: if (colorModePreference === "auto" || colorModePreference === "light" || colorModePreference === "dark") { localStorageAvailable === true ? localStorage.setItem("colorMode", colorModePreference) : null; colorMode = colorModePreference; } else { localStorageAvailable === true ? colorMode = localStorage.getItem("colorMode") : null; }; switch (colorMode) { case "auto": window.matchMedia("(prefers-color-scheme: dark)").matches ? switchRootClass("darkMode") : switchRootClass("lightMode"); //AddEventListeners to window so that it also updates when browser's color mode is altered: const darkModePreference = window.matchMedia("(prefers-color-scheme: dark)"); const lightModePreference = window.matchMedia("(prefers-color-scheme: light)"); darkModePreference.addEventListener("change", function darkModeListener(e){e.matches && colorMode === "auto" ? updateColorMode(false, "auto") : null}); lightModePreference.addEventListener("change", function lightModeListener(e){e.matches && colorMode === "auto" ? updateColorMode(false, "auto") : null}); break; case "light": switchRootClass("lightMode"); break; case "dark": switchRootClass("darkMode"); break; default: break; }; }
原主JS文件代码:
import { updateColorMode } from './colorModeModule.js'; document.onreadystatechange = () => { if (document.readyState === 'complete'){ updateColorMode(true, null); } };
问题根源
你的脚本是等到document.readyState === 'complete'才执行,这时候页面已经完成渲染,默认的:root样式已经显示出来,之后JS才修改<html>的类,导致视觉闪烁。
解决方案
把主题判断的核心逻辑内联到HTML的<head>标签中,在CSS加载前就设置好<html>的类,避免默认样式渲染。
- 修改HTML结构,在
<head>里添加内联脚本(放在CSS之前):
<head> <!-- 内联脚本优先执行,提前设置主题类 --> <script> // 检查localStorage可用性 function checkLocalStorage() { try { localStorage.setItem("test", "test"); localStorage.removeItem("test"); return true; } catch (e) { return false; } } const root = document.documentElement; let colorMode = "auto"; if (checkLocalStorage()) { colorMode = localStorage.getItem("colorMode") || "auto"; } // 直接设置主题类 if (colorMode === "dark") { root.classList.add("darkMode"); } else if (colorMode === "light") { root.classList.add("lightMode"); } else { // 自动模式根据系统偏好设置 window.matchMedia("(prefers-color-scheme: dark)").matches ? root.classList.add("darkMode") : root.classList.add("lightMode"); } </script> <!-- 然后加载你的CSS --> <link rel="stylesheet" href="your-stylesheet.css"> </head>
- 调整原有的JS模块和主JS:
- 内联脚本已经完成了初始类设置,后续模块只需要处理主题切换的逻辑和系统偏好变化的监听。
- 主JS不需要等到
complete状态,改为在DOM加载后初始化事件监听:
import { updateColorMode } from './colorModeModule.js'; // DOM加载后就初始化,不用等complete document.addEventListener('DOMContentLoaded', () => { updateColorMode(true, null); });
- 优化原模块中的
switchRootClass函数,避免重复判断:
function switchRootClass(className) { const rootClassList = document.documentElement.classList; // 直接替换类,更简洁 rootClassList.remove("lightMode", "darkMode"); rootClassList.add(className); }
效果说明
内联脚本在页面渲染前就执行,提前给<html>设置了正确的主题类,CSS加载后会直接应用对应的主题变量,完全避免默认样式的闪烁问题。
内容的提问来源于stack exchange,提问作者Douglas Florindo
相关产品推荐
相关产品推荐

