最可靠方式是动态创建link并监听load/error事件:必须设rel="stylesheet"、插入head后绑定事件,load表示下载解析完成但不保证样式已应用,跨域需crossorigin,IE9需兼容onreadystatechange;fetch+insertRule无法处理@import、字体路径、media等原生逻辑。

用 link 元素动态加载 CSS 并监听 load 事件
直接操作 DOM 插入 link 是最可靠的方式,浏览器原生支持 load 和 error 事件,比靠轮询或定时器判断更准确。
注意:必须在插入 document.head 后再监听事件,否则可能错过触发时机;且 load 仅在 CSS 文件成功下载并解析后触发(不含跨域资源阻塞时的降级行为)。
-
link必须设置rel="stylesheet",否则不会触发load - 跨域 CSS(如 CDN 资源)若未配
crossorigin,可能静默失败且不触发error - IE10–11 支持
load,但 IE9 及以下只支持onreadystatechange,需兼容时加判断
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = '/styles/theme.css';
link.onload = () => console.log('CSS loaded');
link.onerror = () => console.error('CSS load failed');
document.head.appendChild(link);
为什么不能用 fetch() + insertRule 替代?
虽然能拿到 CSS 文本内容,但无法复现原始文件中 @import、字体声明、相对路径资源(如 url(./img.png))的解析逻辑,浏览器不会自动补全或重解析这些引用。
更关键的是:insertRule 只注入规则,不触发样式层更新的完整生命周期(比如媒体查询重计算、级联顺序重排),容易导致视觉不一致或响应式失效。
立即学习“Java免费学习笔记(深入)”;
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
-
fetch()返回的 CSS 文本里含@import时,浏览器完全忽略——它只在外部文件中生效 - 字体
@font-face的src路径会以当前页面为基准,而非原始 CSS 文件位置,极易 404 - 无法继承原始文件的
media属性(如print、(prefers-color-scheme: dark))
link 的 disabled 属性不是加载控制开关
很多人误以为设 link.disabled = true 再设回 false 能“强制重载”,其实它只是临时禁用样式应用,不触发网络请求或重新解析。已加载的 CSS 规则仍驻留在样式表对象中。
真正需要重载时,必须移除旧 link 并新建一个——但要注意:移除瞬间可能导致样式闪动,尤其在首屏渲染中。
- 重复插入同个
href的link,浏览器会复用缓存,但不会重复触发load - 想强制刷新,得加时间戳参数(如
?t=1715823400)或改用link.setAttribute('href', ...)触发更新 -
disabled对未加载完成的link无意义,load前设它不影响加载过程
加载完成 ≠ 样式已应用到元素
load 事件只表示 CSS 文件就绪,但浏览器可能尚未完成样式计算(style recalc)、布局(layout)或绘制(paint)。如果紧接着读取某个元素的 getComputedStyle,结果可能仍是旧值。
需要确保样式生效,得配合 requestAnimationFrame 或 offsetHeight 强制触发重排:
link.onload = () => {
requestAnimationFrame(() => {
const el = document.querySelector('.theme-target');
console.log(getComputedStyle(el).color); // 此时才是新 CSS 生效后的值
});
};
这个细节常被忽略:动态加载 CSS 后立即操作 DOM 样式,大概率拿到过期快照。

















