onerror 失效主因是二次错误被浏览器阻止,正确写法为 onerror="this.src='/assets/placeholder.svg'; this.onerror=null;",需配合绝对路径、SVG占位图及创建时即时绑定;动态插入图片须提前赋值 onerror;<picture> 兜底依赖内部 <img> 的可用 src;CSS 方案无法感知加载失败,JS 才可靠。

onerror 属性必须写成 this.onerror=null; this.src="..."
直接写 onerror="this.src='fallback.png'" 极大概率失效——因为 fallback 图片再 404,浏览器会阻止第二次 onerror 触发,导致白屏且无日志。真正起作用的写法只有一种:onerror="this.src='/assets/placeholder.svg'; this.onerror=null;"。关键点有三个:this.onerror=null 必须紧接在赋值后;路径推荐绝对路径(以 / 开头)或已验证可用的相对路径;占位图优先用 .svg,体积小、无渲染失败风险。
动态插入的 <img> 要提前绑定 onerror,不能靠插入后再补
用 document.createElement('img') 创建后才调用 img.addEventListener('error', ...),往往来不及——错误在插入 DOM 的瞬间就已发生,事件监听器根本收不到。正确做法是:创建时立刻赋值处理逻辑:img.onerror = () => { img.src = '/fallback.svg'; img.onerror = null; };。如果插入量大,更稳妥的是全局捕获:document.addEventListener('error', e => { if (e.target.tagName === 'IMG' && !e.target.dataset.handled) { e.target.dataset.handled = '1'; e.target.src = 'data:image/svg+xml;base64,...'; } }, true);,true 表示捕获阶段,能覆盖所有后续插入的 <img>。
<picture> 的降级不是自动的,<img> 的 src 必须真实可用
<picture> 不是“多个 <source> 任选其一”,而是按顺序尝试加载,全部失败后仍会渲染内部的 <img>。但很多人把 <img src="broken.jpg"> 当作兜底,结果整个 <picture> 只显示 alt 文本。真正可靠的写法是:<img src="/assets/placeholder.svg" alt="描述">——这个 src 必须指向一个高可用资源,它才是最终防线。别指望浏览器在所有 <source> 失败后“聪明地跳转”到别的地方,它只认这个 <img> 的 src。
纯 CSS 方案只能做视觉兜底,无法响应真实加载失败
用 background-image 或 ::after 显示占位内容,确实不依赖 JS,但有个硬伤:它无法区分“图片正在加载”“加载失败”还是“路径为空”。比如 img[src=""] 或 img[src^="http"] 但实际 404,CSS 完全感知不到。所以这类方案只适合对语义要求低的场景(如卡片封面),且必须配合显式宽高或 aspect-ratio,否则伪元素会错位或不可见。真正在意降级可靠性,就得接受 JS 的介入——毕竟加载失败是个运行时网络事件,不是样式状态。
立即学习“前端免费学习笔记(深入)”;
真正容易被忽略的是:onerror 对空字符串、CORS 拦截、200 状态码的损坏图完全静默。这些情况得靠img.naturalWidth === 0 主动轮询,或服务端统一返回带语义的占位响应来兜底。



















