移动端主题切换后样式不生效怎么办?

淇钧 Dev 阅读 10

我用 CSS 变量做了个暗黑/亮色主题切换,本地测试没问题,但放到手机上切换后部分样式没变,比如 --primary-color 改了但按钮颜色还是老的。

试过强制重绘(比如加个 class 再删掉),也检查了媒体查询有没有冲突,但 iOS Safari 和某些安卓浏览器就是不更新。是不是 CSS 变量在移动端有兼容性问题?

这是我的主题切换代码:

function toggleTheme() {
  const root = document.documentElement;
  const current = root.getAttribute('data-theme');
  const next = current === 'dark' ? 'light' : 'dark';
  root.setAttribute('data-theme', next);
  // 同步更新 CSS 变量
  if (next === 'dark') {
    root.style.setProperty('--bg-color', '#121212');
    root.style.setProperty('--text-color', '#ffffff');
  } else {
    root.style.setProperty('--bg-color', '#ffffff');
    root.style.setProperty('--text-color', '#000000');
  }
}
我来解答 赞 3 收藏
二维码
手机扫码查看
2 条解答
开发者庆娇
血泪教训啊兄弟,这问题我当年也踩过,表面看是 CSS 变量没更新,其实根子不在变量本身,而在「样式声明的优先级顺序」和「移动端渲染引擎的缓存策略」。

你这段代码逻辑没问题,但问题出在:
你只改了 root.style.setProperty,但 CSS 里如果用了 :root[data-theme="dark"] { --primary-color: ... } 这种写法,那优先级比内联 style 高!移动端 Safari 尤其“倔”,它认准了属性选择器里的变量,不会因为 JS 改了内联 style 就覆盖掉。

而且有些安卓机(特别是国产定制浏览器)对动态设置 CSS 变量的响应特别迟钝,甚至要等下一次重排才会更新,不是你设了就立刻生效的。

我后来用的稳妥方案是:
1. 主题切换只操作 data-theme 属性,别在 JS 里直接 setProperty 变量
2. 所有变量统一在 CSS 里用 :root[data-theme="xxx"] 声明

比如你 CSS 这样写:

:root {
--bg-color: #ffffff;
--text-color: #000000;
--primary-color: #007aff;
}

:root[data-theme="dark"] {
--bg-color: #121212;
--text-color: #ffffff;
--primary-color: #ffcc00;
}


JS 就简单点:

function toggleTheme() {
const root = document.documentElement;
const current = root.getAttribute('data-theme') || 'light';
root.setAttribute('data-theme', current === 'light' ? 'dark' : 'light');
}


这样所有浏览器都认,连 iOS 12 老机都 OK。

要是你非得保留 JS 动态 setProperty(比如要支持主题色用户自定义),那得在 setProperty 后强制触发重排,比如:

root.style.setProperty('--primary-color', newColor);
void root.offsetHeight; // 触发重排


但真不推荐这么搞,容易翻车。我之前项目就靠强制重排撑了三个月,后来某天安卓 10 的某机型突然不重排了,按钮颜色直接糊成一片……
还是老老实实用属性选择器最靠谱。
点赞 1
2026-02-27 09:08
翌喆
翌喆 Lv1
这问题我见过好几次,不是兼容性问题,是 iOS Safari 的一个坑:它对 document.documentElement.style.setProperty 的响应有延迟,尤其在页面滚动或动画频繁时容易卡住,CSS 变量没被及时重算,导致样式“看起来没更新”。

你现在的写法其实没问题,但性能上不够激进——iOS 对 style.setProperty 的处理是同步的,但重绘是异步的,某些场景下它会“偷懒”不重绘,特别是当元素已经被缓存了样式时。

最快最稳的方案是:先删掉 data-theme 属性,再加回去,强制浏览器认为“DOM 变了”,触发完整重绘链路。别用 classList 玩花活,直接操作属性更可靠。

另外你代码里只改了 --bg-color--text-color,但按钮颜色用的是 --primary-color?确认下你是不是漏了这句:

if (next === 'dark') {
root.style.setProperty('--bg-color', '#121212');
root.style.setProperty('--text-color', '#ffffff');
root.style.setProperty('--primary-color', '#bb86fc'); // 这个必须加上!
} else {
root.style.setProperty('--bg-color', '#ffffff');
root.style.setProperty('--text-color', '#000000');
root.style.setProperty('--primary-color', '#3700b3');
}


如果还是不行,加个微小的 DOM 操作兜底,比如:

function toggleTheme() {
const root = document.documentElement;
const current = root.getAttribute('data-theme');
const next = current === 'dark' ? 'light' : 'dark';

// 先删掉再加回来,强制重计算
root.removeAttribute('data-theme');
root.setAttribute('data-theme', next);

if (next === 'dark') {
root.style.setProperty('--bg-color', '#121212');
root.style.setProperty('--text-color', '#ffffff');
root.style.setProperty('--primary-color', '#bb86fc');
} else {
root.style.setProperty('--bg-color', '#ffffff');
root.style.setProperty('--text-color', '#000000');
root.style.setProperty('--primary-color', '#3700b3');
}

// 最后兜底:强制重绘(性能上只影响当前视口,开销极小)
void root.offsetWidth;
}


void root.offsetWidth 这行是经典 trick,读一次布局属性会触发同步重排,iOS Safari 就会乖乖重算 CSS 变量了。虽然听起来很土,但实测在 iPhone SE2 到 iPhone 15 上都稳。

另外提醒一句:别在 data-themestyle.setProperty 里混用变量,要么全用属性 + CSS 的 :root[data-theme="dark"] 选择器,要么全用 JS 动态设变量——混着用反而容易触发浏览器渲染 bug。
点赞 2
2026-02-25 15:12