移动端怎么监听系统暗黑模式切换并动态更新UI?
我在做移动端页面,想根据系统暗黑模式自动切换主题色,但用prefers-color-scheme媒体查询只在页面加载时生效,切换系统主题后页面没反应,咋办?
试过加matchMedia监听,但不知道是不是写法不对,完全没触发。有没有人遇到过类似问题?
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
mediaQuery.addEventListener('change', (e) => {
console.log('theme changed', e.matches);
});
我们先来验证一下:先别急着加逻辑,先看事件到底有没有触发。
你可以先写个最简单的测试页,打开手机浏览器访问,然后切系统暗黑模式,看控制台有没有输出:
注意啊,这里有个坑:Safari(包括 iOS Safari 和 macOS Safari)在早期版本里只支持
addListener和removeListener,addEventListener是后来才加的。虽然新版本 Safari 已经支持addEventListener,但为了稳,建议两个都加,或者做一次兼容封装。第二步,写个兼容性更强的监听函数,把主题切换逻辑抽出来:
然后你就可以这样用:
第三步,别忘了在 CSS 里配合使用变量或 class 控制样式。比如你可以在
上加dark类,然后这样写 CSS:或者直接用媒体查询写死样式(但这样就失去动态切换能力了):
但你问题里说“只在页面加载时生效”,说明你可能只写了 CSS 媒体查询,没加 JS 动态监听。所以必须用 JS 去监听变化并更新 DOM 或 CSS 变量。
第四步,补充一个常见问题:iOS Safari 在后台切了暗黑模式,再切回前台才触发事件。也就是说,如果你切了系统主题但没切回浏览器页面,可能要等几秒甚至切换页面后才生效。这是 Safari 的省电策略,不是 bug,但你得知道——它不是不触发,是延迟触发。
你可以加个
visibilitychange事件兜底,当用户切回页面时强制刷新一次主题判断:最后,如果你用的是 Vue、React 这类框架,建议把主题监听逻辑封装成一个 hook 或指令,避免在多个组件里重复写。比如 React 里可以这样:
你试试这些步骤,特别是先加 console.log 看事件有没有触发——我见过不少同学以为没触发,其实是浏览器控制台没开,或者切主题时页面没在前台。要是还有问题,把你的具体代码贴出来,我帮你看看是不是 event listener 挂错了地方或者被覆盖了。