link.onload不可靠,应组合onload、onerror、document.styleSheets轮询及超时兜底;因缓存、跨域、浏览器兼容性导致事件不触发,需try/catch访问cssRules并设50ms轮询与10秒超时。

link.onload 不可靠,尤其在缓存命中或跨域时可能根本不触发。真正能落地的方案是组合使用 onload、onerror 和 document.styleSheets 检查,再加一层超时兜底。
为什么 link.onload 会失效
Chrome/Firefox 对本地缓存的 CSS 文件常跳过 load 事件;Safari 在某些版本中对 rel="stylesheet" 的 link 元素不触发 onload;跨域 CSS 因 CORS 限制,即使加载成功也无法读取规则,但事件也不一定触发。
常见错误现象:console.log('loaded') 从不执行,但样式确实生效了。
用 document.styleSheets 主动轮询是否加载完成
核心逻辑是:拿到刚插入的 link 元素后,遍历 document.styleSheets 找到匹配 href 的项,再尝试读取其 cssRules(注意不是 rules,IE 已淘汰)——只要能读到规则数 ≥ 0,就说明已解析完毕。
要点:
- 必须用
try/catch包裹sheet.cssRules访问,跨域 CSS 会抛SecurityError - 轮询间隔建议设为
50ms,太短浪费 CPU,太长响应滞后 - 检查前先确认
sheet.href === link.href,避免误判内联或其他外部表 - 若
sheet.disabled === true,跳过该表(比如媒体查询不匹配时)
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = '/theme/dark.css';
document.head.appendChild(link);
function waitForCSS() {
for (let i = 0; i = 0) {
console.log('CSS loaded and parsed');
return;
}
} catch (e) {
// 跨域,但至少说明 link 已插入并开始加载
console.warn('Cross-origin CSS loaded, rules inaccessible');
return;
}
}
}
setTimeout(waitForCSS, 50);
}
waitForCSS();
必须加超时兜底,否则可能无限等待
网络中断、404、服务端返回空 CSS 等情况会导致轮询永远等不到 cssRules。实际项目里,10 秒是合理上限。
操作建议:
- 启动轮询时同时启动
setTimeout,超时后清除轮询并触发失败回调 - 把
link.onerror和超时逻辑合并处理,统一标记为“加载失败” - 失败后可 fallback 到备用样式 URL,或降级为内联基础样式
- 不要依赖
link.sheet属性 —— 它在部分浏览器中异步赋值,且 Safari 有时为null
现代替代方案:用 PerformanceObserver 补充验证
PerformanceObserver 能捕获资源加载完成事件,比轮询更轻量,但不能区分“加载完成”和“解析完成”。它适合做辅助确认,而非唯一依据。
用法要点:
- 监听
entryTypes: ['resource'],过滤entry.initiatorType === 'link'且entry.name以.css结尾 - 仅当
entry.duration > 0且未被onerror或超时拦截时,才认为“网络层加载成功” - 仍需配合
document.styleSheets检查,因为资源加载完 ≠ 样式已注入 DOM 渲染树
真正难的不是监听,而是判断“完成”的定义:你是要等网络请求结束?等样式表解析完成?还是等浏览器应用样式并触发重排?多数业务场景下,只有等到 cssRules 可读,才能安全地调用 getComputedStyle 或基于该 CSS 的 JS 逻辑 —— 这一点很容易被忽略。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











