真正跨浏览器可用的方案是事件监听+超时兜底+属性轮询三重保障:onload/onerror需插入前绑定,5秒超时后轮询testel.opacity等稳定属性,避免依赖sheet.cssrules或单一事件。

link.onload 不可靠,link.onerror 盲区大,单靠轮询 sheet.cssRules 会报跨域错误或在 Safari 下失效。真正能落地的方案是三者组合:事件监听 + 超时兜底 + 属性轮询。
为什么不能只用 link.onload
Chrome/Firefox/Safari 都要求 onload 必须在 link 插入 DOM 前绑定,否则事件可能永远不触发(尤其缓存命中时)。Safari 还存在 onload 永远 pending 的 bug,导致 Promise 卡死。IE 虽支持 attachEvent('onload'),但 404 时也会误触发,无法区分成功/失败。
-
link.onload = handler必须写在document.head.appendChild(link)之前 - 插入后才赋值,等于白写;加
setTimeout延迟赋值也救不了竞态 - 即使绑对了,Safari 仍可能不触发,必须有 fallback
为什么 link.onerror 不能当失败依据
onerror 只对明确的网络/解析错误敏感:404、CORS 阻断、MIME 类型错误(如返回 HTML)、CSS 语法错误。但它对以下情况完全沉默:
- 网络中断或请求超时 → 无任何事件
- Safari 加载失败时行为不一致,有时触发、有时不触发
- 服务端返回 200 状态码,但响应体为空、乱码或 JSON → 样式无效,
onerror不触发 - CDN 回源失败返回兜底页(如 Nginx 50x 页面)→ 200 + HTML 内容,
onerror完全失灵
怎样做才是跨浏览器可用的检测
用「事件监听 + 5 秒超时 + 属性轮询」三重保障,缺一不可:
- 插入前立即绑定
link.onload和link.onerror - 同步启动一个
setTimeout(..., 5000),超时即进入轮询阶段 - 轮询逻辑检查
link.sheet是否存在,再尝试读取link.sheet.cssRules(捕获 NS_ERROR_DOM_SECURITY_ERR);WebKit 下直接看link.sheet是否为非 null 对象 - 轮询间隔设为 10ms,避免阻塞主线程;一旦确认加载完成,用
setTimeout(cb, 1)微任务延迟回调,确保样式已应用
示例关键片段(不依赖框架):
function loadCss(src, cb) {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = src;
let loaded = false;
const done = (err, node) => {
if (loaded) return;
loaded = true;
cb(err, node);
};
link.onload = () => done(null, link);
link.onerror = () => done(new Error('CSS load failed'), link);
document.head.appendChild(link);
setTimeout(() => {
if (loaded) return;
// 开始轮询
const poll = () => {
try {
if (link.sheet && ('cssRules' in link.sheet || /webkit/i.test(navigator.userAgent))) {
return setTimeout(() => done(null, link), 1);
}
} catch (e) {
if (e.name === 'SecurityError') {
return setTimeout(() => done(null, link), 1);
}
}
setTimeout(poll, 10);
};
poll();
}, 5000);
}
轮询时该检查哪个 CSS 属性最稳妥
别用 getComputedStyle(el).width 或 height —— 容易被其他样式覆盖,结果不可靠。推荐用带副作用且不易被覆盖的属性:
-
getComputedStyle(testEl).opacity:默认为'1',若 CSS 中显式设置了opacity: 0.5,则可确认规则已生效 - 提前在 CSS 中定义一个唯一 class,比如
.css-loaded-test { opacity: 0.001; },然后创建一个隐藏<div class="css-loaded-test"></div>,轮询其opacity是否变为'0.001' - 这个方法绕过
sheet.cssRules的跨域限制,且在所有现代浏览器中行为一致
真正难的不是写轮询逻辑,而是意识到:CSS 加载状态无法被单一信号穷举。你得接受“没有银弹”,只能靠组合策略覆盖边缘 case —— 尤其是 Safari 和跨域场景,最容易掉坑里。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











