Image Load Error Handler

Preface

In the past, I posted an answer in SO about how to replace broken images. And the code is

window.addEventListener(
  'error',
  windowErrorCb,
  {
    capture: true
  },
  true
)

function windowErrorCb(event) {
  let target = event.target
  let isImg = target.tagName.toLowerCase() === 'img'
  if (isImg) {
    imgErrorCb()
    return
  }

  function imgErrorCb() {
    let isImgErrorHandled = target.hasAttribute('data-src-error')
    if (!isImgErrorHandled) {
      target.setAttribute('data-src-error', 'handled')
      target.src = 'backup.png'
    } else {
      //anything you want to do
      console.log(target.alt, 'both origin and backup image fail to load!')
    }
  }
}

And the point is :

  1. Put the code in the head and executed as the first inline script. So, it will listen the errors happen after the script.
  2. Use event capturing to catch the errors earlier, also for those events which don't bubble.
  3. Use event delegation which avoids binding events on each image.
  4. Give the error img element an attribute after giving them a backup.png to avoid error of the backup.png and subsequent infinite loop like below:
img error->backup.png->error->backup.png->error->,,,,,

I thought the answer is almost perfect. But it turns out there is more scenes I don't know.

No Src?

Until Chrome 70.0.3538.102 in win7, code below wouldn't trigger error event.

<img />

But code below would trigger!

<img src="" /> <img src />

That does make sense. However, img without src wouldn't hidden. They would have 1px border like:

image

In this case, we may have to do something about that. For instance,

img:not([src]) {
  opacity: 0;
}

or a default backgroundImage which will work on img though 1px border is still there.

img:not([src]) {
  background: no-repeat left top / cover;
  background-image: linear-gradient(to right, lightblue, pink); /*or your logo*/
}
BackgroundImage Error?

I still can't find a perfect solution for backgroundImage. The best answer in SO is like:

.app__bg_img_box {
  background: no-repeat left top / cover;
  background-image: url(./images/github.png), linear-gradient(to right, lightblue, pink);
  /* you can also use default.png like code below */
  /* background-image: url(./images/github.png), url(./images/default.png); */
}

And the cons is obvious.

image

  1. You have to take care of the transparency problem of the target image.
  2. Users can see the toggle between target image and default image.
  3. A little flaw is that default image will always be downloaded.

Another way I figured out is like code below. Here is the simplest code:

let backgroundDivs = Array.from(document.querySelectorAll('.app__bg_img_box'))
backgroundDivs.forEach(div => {
  let imgUrl = window
    .getComputedStyle(div)
    .backgroundImage.match(/url\(["']?(.*)["']?\)/)
  if (imgUrl) {
    let img = new Image()
    img.src = imgUrl[1]
    img.onerror = function(event) {
      div.style.backgroundImage = 'url(./images/default.png)'
      img.onerror = null
    }
  }
})

It will work well in most simple scenes but the cons is also obvious:

  1. Code will be more complicated if you want to deal with multiple backgroundImages.
  2. Each time you updated your dom structure, you may have to run the code above if you have add new divs with backgroundImage.

Ending

If lucky enough, we may have the new API in Images which would make code below work.

background: image('target.gif', 'fallback.gif');

Reference

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值