link.onload在Chrome/Firefox/Edge中可用但须在append前绑定,Safari完全不支持;应组合onload/onerror、sheet.cssRules检测(需try/catch SecurityError)及requestAnimationFrame轮询+5秒超时兜底。

link.onload 在 Chrome/Firefox/Edge 中能用,但必须在 append 前绑定
Chrome 93+、Firefox 65+、Edge(Chromium)确实支持 link.onload,但它有个硬性前提:监听器必须在 link 插入 DOM 之前设置。如果先 document.head.appendChild(link) 再赋值 link.onload,缓存命中或加载极快时事件会直接丢失——规范要求监听器就位早于资源获取启动。
正确写法是:
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = '/style.css';
link.onload = () => console.log('✅ 加载成功');
link.onerror = () => console.log('❌ 加载失败');
// ✅ 必须在这一步之前设置事件
document.head.appendChild(link);
常见错误包括:用 setTimeout 延迟赋值、在 append 后链式调用、或误以为“只要加了就能捕获”。
Safari 完全不触发 link.onload,必须换方案
截至 2026 年 9 月,Safari(包括 iOS 17+/macOS 14+)仍**完全不派发 link.onload 事件**——这不是 bug,是 WebKit 主动未实现该规范行为。只依赖 onload 的代码在 Safari 下会永远卡住,Promise 不 resolve,逻辑阻塞。
立即学习“前端免费学习笔记(深入)”;
因此,生产环境不能把 onload 当唯一依据。可行的替代路径有两条:
- 轮询
getComputedStyle(testEl).opacity等稳定 CSS 属性(需提前插入一个带预期样式的测试元素) - 检查
link.sheet?.cssRules是否可读且非空(注意跨域时会抛SecurityError)
推荐组合使用:先设 onload/onerror,同时启动 requestAnimationFrame 轮询,5 秒超时兜底。
用 sheet.cssRules 检测要处理 SecurityError 和竞态
link.sheet?.cssRules 是最接近“原生检测”的属性,但它在跨域 CSS 或未加载完成时会抛出 SecurityError(code 1000),或返回 undefined。不能直接访问,必须 try/catch。
轮询逻辑要点:
- 只对同源 CSS 有效;跨域时
sheet可能存在但cssRules不可读 - 首次检查可能过早,
link.sheet为null,需等待它“出现” - 即使
cssRules.length > 0,也不能保证所有规则已解析完毕(比如含@import)
最小安全检查示例:
function isCSSLoaded(link) {
try {
return link.sheet && link.sheet.cssRules?.length > 0;
} catch (e) {
if (e.code === 1000) return true; // SecurityError → 视为已加载(同源策略限制,但文件已进样式表)
return false;
}
}
不要只靠 onerror 判断失败
link.onerror 仅对明确的网络/响应错误生效:404、CORS 阻断、MIME 类型错误(如返回 HTML)、语法错误等。但它对以下情况完全静默:
- 网络中断(请求未发出或超时)
- 服务端返回 200 + 空内容 / 乱码 CSS
- Safari 下部分失败场景不触发
onerror(行为不稳定)
这意味着:onerror 不触发 ≠ 加载成功,也不代表“还在加载”。它只是失败信号的子集,不能反推状态。真正可靠的失败判断,得靠超时 + 样式检测双验证。
跨浏览器兼容的加载检测,核心不在“怎么写”,而在“怎么兜底”——事件监听只是第一层,轮询和超时才是让逻辑不卡死的关键。漏掉 Safari 或忽略 SecurityError,都可能导致线上页面白屏或样式延迟应用。


















