1.直接在img标签上使用内联事件处理图片加载失败的情况
<img src='xxxxx' onerror="this.src = 'default.png'">
但是这种方式代码侵入性太大,指不定就会有地方漏掉。
全局img添加事件
const imgs = document.getElementsByTagName('img')
Array.prototype.forEach.call(imgs, img => {
img.addEventListener('error', e => {
e.target.src = 'default.png'
})
})
2.利用error事件捕获
document.addEventListener( 'error', e => {
let target = e.target
const tagName = target.tagName || ''
if (tagName.toLowerCase = 'img') {
target.src = 'default.png'
}
target = null
}, true )
上面的方案有两个缺点:
- 如果是因为网络差导致加载失败,那么加载默认图片的时候也极大概率会失败,于是会陷入无限循环。
- 如果是网络波动导致的加载失败,那么图片可能重试就会加载成功。
所以我们可以为每个img标签额外添加一个data-retry-times计数属性,当重试超过限制次数后就用base64图片作为默认兜底。
3.添加data-retry-times计数属性
document.addEventListener( 'error', e => {
let target = e.target
const tagName = target.tagName || ''
const curTimes = Number(target.dataset.retryTimes) || 0
if (tagName.toLowerCase() === 'img') {
if (curTimes >= 3) {
target.src = 'data:image/png;base64,xxxxxx'
} else {
target.dataset.retryTimes = curTimes + 1
target.src = target.src
}
}
target = null
}, true )
4.CSS处理的最优解
上面方式是采用替换src的方式来展示兜底图,这种解决方式有一个缺陷:
- 原图的资源链接无法从标签上获取(虽然可以通过加data-xxx属性的方式hack解决)。
所以还有一种更好的方式,就是利用CSS伪元素::before和::after覆盖原本元素,直接展示兜底base64图片。
css样式如下
img.error {
display: inline-block;
transform: scale(1);
content: '';
color: transparent;
}
img.error::before {
content: '';
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: #f5f5f5 url(data:image/png;base64,xxxxxx) no-repeat center / 50% 50%;
}
img.error::after {
content: attr(alt);
position: absolute;
left: 0;
bottom: 0;
width: 100%;
line-height: 2;
background-color: rgba(0,0,0,.5);
color: white;
font-size: 12px;
text-align: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
js代码如下
document.addEventListener( 'error', e => {
let target = e.target
const tagName = target.tagName || ''
const curTimes = Number(target.dataset.retryTimes) || 0
if (tagName.toLowerCase() === 'img') {
if (curTimes >= 3) {
target.classList.remove('error')
target.classList.add('error')
} else {
target.dataset.retryTimes = curTimes + 1
target.src = target.src
}
}
target = null
}, true )