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,避免误判内联<style></style>或其他外部表 - 若
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 < document.styleSheets.length; i++) {
const sheet = document.styleSheets[i];
if (sheet.href === link.href && !sheet.disabled) {
try {
if (sheet.cssRules && sheet.cssRules.length >= 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 秒是合理上限。
立即学习“Java免费学习笔记(深入)”;
操作建议:
- 启动轮询时同时启动
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 逻辑 —— 这一点很容易被忽略。


















