原文 :https://juejin.im/post/5e1c143c5188254d9c51698b?utm_source=gold_browser_extension
如何优雅处理图片异常
前言
不同浏览器对加载失败图片的图标展示不统一,所以给定一个默认的失败图片就尤为重要。
正好前几天处理了一下公司首页图片 error 默认图片的问题,就趁着记忆没有消失分享一下这篇文章。
error
img 标签有一个 error 事件,通过它我们可以捕捉到异常,使用起来也很简单
<img src="abc.xxx" alt="xxx" class="cs-img" />
复制代码
<img src="abc.xxx" alt="xxx" class="cs-img" onerror="this.src = 'xxxx.png'" />;
// 或者
var img = document.querySelector(".cs-img");
img.addEventListener("error", function(e) {
e.target.src = "xxxx.png";
});
复制代码
全局
上面的方法没有问题,不过需要我们手动管理图片这样写的话维护成本可能很高,有可能你只是想为图片失败统一处理。
我们希望可以统一监听到error
的事件来完成,不过图片的error
处于事件模型的第二阶段也就是目标阶段,是不会向上冒泡的,但是也是可以通过window.addEventListener.error
来完成监听。
顺便说下
window.addEventListener.error
不仅可以监听到图片的失败也可以监听到css,js
之类加载错误,注意区分window.onerror
和window.addEventListener.error
之间的区别,前者是 js 运行错误,后者是资源错误,对于事件模型网上的例子很多,这里不做展开来说了。
来看下如何用
window.addEventListener(
"error",
function(event) {
var dom = event.target;
if (/img/i.test(dom.nodeName)) {
dom.src = "xxxx.png";
}
},
true
);
复制代码
注意 addEventListener
第二个参数必须是 true
,默认为 false
,表示只在冒泡阶段出发,但是上面我们说过图片的 error 事件并不会向上冒泡所以是不会捕捉到的。
重试次数
上面的代码没有考虑到备选src
也会失效的问题,如果备选src
失效就会导致图片无限重试,下面就抛砖引玉写一种方法。
window.addEventListener(
"error",
function(event) {
var dom = event.target;
if (!/img/i.test(dom.nodeName)) {
return;
}
// 不存在返回null
var retry = +dom.getAttribute("data-retry");
if (retry >= 3) {
// 绝对安全的图片
dom.src =
"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
} else {
dom.src = "xxxx.png";
dom.setAttribute("data-retry", (retry += 1));
}
},
true
);
复制代码
动态填充
最后分享一个动态模板渲染出来的网页(例如,网页的内容是从后台的编辑器 html 直接插入的)如何监听error
,如果不考虑兼容问题可以在head
使用window.addEventListener.error
方法,但是如果需要兼容性很高不妨试试下面这种。
const img = Array.prototype.slice.call(document.images);
for (let i = 0; i < img.length; i++) {
var dom = img[i];
const image = document.createElement("img");
image.src = dom.src;
image.style.display = "none";
document.body.appendChild(image);
image.onerror = function(event) {
dom.src = "xxxx.png";
document.body.removeChild(image);
};
image.onload = function() {
document.body.removeChild(image);
};
}
复制代码
上面我们说了假设内容是直接通过 html 代码插入的,我们可能监听不到图片的默认错误事件,那么我们可以在网页加载完成之后重试一遍所有的 img,在为 img 指定一次错误事件就 OK 了。
最后
为了方便演示,上面的代码我都没有做兼容性的补充,但是在实际生产中上面代码需要polyfill
尤其是 dom 的一些语法,推荐简单一些的做法可以把上面的例子转化为 jquery 的语法。