应使用 window.matchmedia 监听 (prefers-color-scheme: dark) 的 change 事件,立即执行并清理监听器,结合 css 变量统一管理滤镜值,通过 data-auto-filter 属性按需控制元素,兼顾兼容性与 ssr 安全。

用 window.matchMedia 监听系统深色/浅色模式切换,并在变化时自动更新 CSS 滤镜(如 filter: invert(1) hue-rotate(180deg)),关键在于将媒体查询状态映射为可复用的视觉策略,而非直接硬编码滤镜值。滤镜更新应作为主题响应的一部分,与 CSS 变量或 class 切换协同工作,避免内联样式污染和性能抖动。
监听系统主题并触发滤镜逻辑
必须监听 (prefers-color-scheme: dark) 的 change 事件,不能只读一次 matches:
- 调用
const mql = window.matchMedia('(prefers-color-scheme: dark)')获取媒体查询对象 - 立即执行一次处理函数,确保首屏渲染即应用正确滤镜
- 绑定
mql.addEventListener('change', e => { updateFilter(e.matches) }),注意使用标准事件 API,不使用已废弃的addListener或onchange赋值 - 在页面卸载或组件销毁时调用
mql.removeEventListener('change', handler),防止内存泄漏
用 CSS 变量统一管理滤镜值
把滤镜声明抽离到 :root 中,由媒体查询自动覆盖,JS 只负责通知状态,不操作样式:
- 在根元素定义默认滤镜:
:root { --theme-filter: none; } - 在深色模式下覆盖:
@media (prefers-color-scheme: dark) { :root { --theme-filter: invert(0.85) contrast(1.1); } } - 目标元素使用:
img, .icon { filter: var(--theme-filter); } - 这样既支持系统切换,也兼容手动设置(如通过
document.documentElement.setAttribute('data-theme', 'dark')触发)
针对特定元素动态控制滤镜
若需对某些图片、SVG 或 canvas 单独启用/禁用滤镜(例如避免深色模式下图标过度反色),可在监听回调中精细控制:
- 给需要响应的元素添加
data-auto-filter属性,便于批量选取 - 在
updateFilter(isDark)函数中,仅对带该属性的元素设置style.filter,或切换.filteredclass - 对 SVG 内联图,务必移除
fill/stroke等内联颜色属性,否则滤镜可能被覆盖或失效 - 避免在回调中频繁操作大量 DOM;如需高性能更新,可用
requestIdleCallback延后执行
兼容性与降级处理
不是所有环境都支持 prefers-color-scheme,需提供 fallback:
- 检查支持性:
if (!window.matchMedia || !window.matchMedia('(prefers-color-scheme)').media),不支持时跳过监听 - 降级方案:读取
localStorage.getItem('color_mode'),或默认关闭滤镜 - 部分安卓 WebView 或旧版 Safari 可能不触发
change事件,可加轻量兜底(如每 3 秒检测一次mql.matches),但仅限必要场景 - 服务端渲染(SSR)环境下,需包裹
typeof window !== 'undefined'判断,避免报错











