onload 不触发主因是资源未成功加载或事件监听顺序错误;应先绑定 addEventListener 再设置 src,并对 img 主动调用 decode() 确保解码完成。
img 的 onload 为什么有时不触发?
因为
只在资源**成功加载完成**时触发,且前提是 DOM 中该
元素已存在、
已设置、浏览器尚未缓存命中并同步完成解码(极少数情况)。
如果
是空字符串、
URI 解析失败、或图片格式损坏,
不会触发,
会触发
如果元素插入 DOM 前就设置了
,但
是之后才赋值的,没问题;但如果
赋值前图片已被缓存且浏览器提前加载完毕(如预加载),
可能已错过 —— 此时需检查
属性
动态创建
时,务必先设
再设
,顺序反了在某些浏览器(尤其是旧版 Safari)中可能丢失事件
script 的 onload 和 defer/async 的关系
仅对**外部脚本**(带
)有效,且只在脚本下载、解析、执行全部完成后触发。它和
或
并非互斥,但行为有关键差异:
:脚本异步下载,完成后立即执行,
在执行结束时触发;多个
脚本不保证顺序
:脚本异步下载,但要等到 DOM 解析完成(
前)才按顺序执行,
在其执行完后触发
没加
/
的外部脚本:阻塞 HTML 解析,
在其执行完后触发 —— 但此时页面渲染已严重延迟,不推荐
替代方案:用 addEventListener 更可靠
直接写
属性容易被覆盖,也不支持多个监听器。现代写法应优先用
:
对
同理:
事件监听器不会被重复赋值覆盖
可多次调用
绑定不同逻辑
注意:IE9–IE11 不支持
,但支持
(需判断
)
资源加载完成 ≠ 页面可用,别忽略 decode
尤其对
,
触发只代表图像数据已下载并解析为位图,但**尚未完成解码(decode)** —— 这在大图、WebP/AVIF 等格式上可能明显卡顿。用户滚动到图片位置时仍可能看到空白或重排。
真正“可安全绘制”的时机是
Promise 完成后(现代浏览器支持)
例如:
不调用
时,首次绘制仍会隐式触发解码,可能造成卡顿;主动调用可将耗时前置,配合 loading 状态更可控
这个点常被忽略:onload 是加载链路的中间节点,不是终点。
onloadsrcsrcdata:onloadonerroronloadsrcsrconloadcompleteonloadsrcsrcdeferasyncasynconloadasyncdeferDOMContentLoadedonloadasyncdeferonloadonloadaddEventListenerconst img = document.createElement('img');
img.addEventListener('load', () => console.log('loaded'));
img.addEventListener('error', () => console.log('failed'));
img.src = '/path/to/image.jpg'; // 注意:必须放最后const script = document.createElement('script');
script.addEventListener('load', () => console.log('script executed'));
script.src = '/path/to/app.js';
document.head.appendChild(script);addEventListenerscript.onloadscript.onreadystatechangescript.readyState === 'loaded' || script.readyState === 'complete'onloadimg.decode()img.addEventListener('load', async () => {
try {
await img.decode(); // 等待 GPU 解码完成
img.classList.add('ready');
} catch (e) {
console.error('decode failed', e);
}
});decode()