onerror监听器需在head顶部用addEventListener(true)注册,避免被覆盖;动态script须用DOM API创建并提前绑定onerror;跨域错误仅能通过target.tagName识别。

onerror 事件监听器被反复触发却没进断点?
常见现象是:给 img 或 script 加了 onerror,但资源连续失败时回调只执行一次,或在调试器里设了断点却根本不停——不是代码没跑,而是监听器被后续脚本覆盖或注册时机不对。
根本原因在于:window.addEventListener('error', handler, true) 的捕获阶段监听器,如果没加 true 参数,就只能捕获冒泡阶段的错误(比如 JS 运行时异常),而 HTML 解析错误、资源加载失败(如 404 图片)必须靠捕获阶段才能拿到。更隐蔽的是:第三方 SDK、埋点脚本常在你之后执行 removeEventListener 或直接重写 window.onerror,导致你的监听器“消失”。
- 确保监听代码放在
最顶部,早于所有外部脚本(包括 CDN 加载的监控 SDK) - 必须传第三个参数
true,否则对<img src="x" alt="HTML开发者工具如何跟踪onerror循环" >这类标签级加载失败完全无感 - 避免用
window.onerror = function() {...}赋值方式,它会被后来的赋值直接覆盖;改用addEventListener并保存引用,方便后续检查是否还存在
动态插入的 script 标签 onerror 不触发?
用 innerHTML += '<script src="a.js"></script>' 插入脚本后,onerror 属性不生效,控制台报错但监听器压根不进——这不是 bug,是规范行为。浏览器对 innerHTML 插入的 script 会忽略内联事件属性(包括 onerror、onload),只执行脚本内容本身。
正确做法是用 DOM API 显式创建并绑定:
立即学习“前端免费学习笔记(深入)”;
const s = document.createElement('script');
s.src = 'a.js';
s.onerror = () => console.error('script load failed:', s.src);
document.head.appendChild(s);
- 不要用
innerHTML插入含<script></script>的字符串,这是最常见误用点 -
s.onerror必须在appendChild前设置,否则可能因异步加载已触发失败而错过回调 - 若脚本需动态路径,记得先清空
s.src再赋新值,否则浏览器可能缓存 404 响应,重试无效
如何确认 onerror 是被覆盖还是根本没注册?
打开开发者工具 Console 面板,直接执行以下检查语句:
getEventListeners(window).error
如果返回空数组,说明没监听器;如果返回一个数组但长度为 0 或只有非你写的回调,大概率被覆盖。注意:这个 API 只在 Chrome/Edge DevTools 中可用,Firefox 不支持。
- 在页面加载初期(比如
<script></script>放在里)立即运行console.log(getEventListeners(window).error),比等页面加载完再查更可靠 - 搜索页面源码中是否有
window.onerror =或removeEventListener('error'的调用,尤其注意广告、A/B 测试、性能监控类 SDK 的初始化逻辑 - 如果用了沙箱 iframe(如在线编辑器),主页面的
getEventListeners查不到 iframe 内部监听器,必须在 iframe 上下文里执行
onerror 触发但堆栈为空?
特别是跨域脚本(CDN 上的 JS)加载失败时,event.error 是 undefined,event.message 是空字符串,只靠 e.target.tagName === 'SCRIPT' 和 !(e instanceof ErrorEvent) 才能准确识别——这是唯一靠谱的判断组合。
- 别依赖
e.filename或e.lineno,跨域场景下它们全为空 - 用
new URL(e.target.src, location.href).pathname标准化路径,用于重试计数或上报分类 - 如果要重试,必须先设
e.target.src = '',再赋新值,否则浏览器跳过请求(哪怕你改了 URL)
onerror 就只是个摆设。



















