jQuery/JavaScript to replace broken images

Viewed 313131

I have a web page that includes a bunch of images. Sometimes the image isn't available, so a broken image is displayed in the client's browser.

How do I use jQuery to get the set of images, filter it to broken images then replace the src?


--I thought it would be easier to do this with jQuery, but it turned out much easier to just use a pure JavaScript solution, that is, the one provided by Prestaul.

32 Answers

Handle the onError event for the image to reassign its source using JavaScript:

function imgError(image) {
    image.onerror = "";
    image.src = "/images/noimage.gif";
    return true;
}
<img src="image.png" onerror="imgError(this);"/>

Or without a JavaScript function:

<img src="image.png" onError="this.onerror=null;this.src='/images/noimage.gif';" />

The following compatibility table lists the browsers that support the error facility:

http://www.quirksmode.org/dom/events/error.html

I use the built in error handler:

$("img").error(function () {
    $(this).unbind("error").attr("src", "broken.gif");
});

Edit: The error() method is deprecated in jquery 1.8 and higher. Instead, you should use .on("error") instead:

$("img").on("error", function () {
    $(this).attr("src", "broken.gif");
});

Here is a standalone solution:

$(window).load(function() {
  $('img').each(function() {
    if ( !this.complete
    ||   typeof this.naturalWidth == "undefined"
    ||   this.naturalWidth == 0                  ) {
      // image was broken, replace with your new image
      this.src = 'http://www.tranism.com/weblog/images/broken_ipod.gif';
    }
  });
});

I believe this is what you're after: jQuery.Preload

Here's the example code from the demo, you specify the loading and not found images and you're all set:

jQuery('#images img').preload({
  placeholder:'placeholder.jpg',
  notFound:'notfound.jpg'
});

While the OP was looking to replace the SRC, I'm sure many people hitting this question may only wish to hide the broken image, in which case this simple solution worked great for me.

Using Inline JavaScript:

<img src="img.jpg" onerror="this.style.display='none';" />

Using External JavaScript:

var images = document.querySelectorAll('img');

for (var i = 0; i < images.length; i++) {
  images[i].onerror = function() {
    this.style.display='none';
  }
}
<img src='img.jpg' />

Using Modern External JavaScript:

document.querySelectorAll('img').forEach((img) => {
  img.onerror = function() {
    this.style.display = 'none';
  }
});
<img src='img.jpg' />

See browser support for NodeList.forEach and arrow functions.

I am not sure if there is a better way, but I can think of a hack to get it - you could Ajax post to the img URL, and parse the response to see if the image actually came back. If it came back as a 404 or something, then swap out the img. Though I expect this to be quite slow.

Sometimes using the error event is not feasible, e.g. because you're trying to do something on a page that’s already loaded, such as when you’re running code via the console, a bookmarklet, or a script loaded asynchronously. In that case, checking that img.naturalWidth and img.naturalHeight are 0 seems to do the trick.

For example, here's a snippet to reload all broken images from the console:

$$("img").forEach(img => {
  if (!img.naturalWidth && !img.naturalHeight) {
    img.src = img.src;
  }
}

I think I have a more elegant way with event delegation and event capturing on window's error even when the backup image fail to load.

img {
  width: 100px;
  height: 100px;
}
<script>
  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!');
      }
    }
  }
</script>
<img id="img" src="error1.png" alt="error1">
<img id="img" src="error2.png" alt="error2">
<img id="img" src="https://i.stack.imgur.com/ZXCE2.jpg" alt="avatar">

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, especially 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 disappearance of the backup.png and subsequent infinite loop like below:

img error->backup.png->error->backup.png->error->,,,,,

For React Developers:

<img 
    src={"https://urlto/yourimage.png"} // <--- If this image src fail to load, onError function will be called, where you can add placeholder image or any image you want to load
    width={200} 
    alt={"Image"} 
    onError={(event) => {
       event.target.onerror = "";
       event.target.src = "anyplaceholderimageUrlorPath"
       return true;
    }}
 />

I got the same problem. This code works well on my case.

// Replace broken images by a default img
$('img').each(function(){
    if($(this).attr('src') === ''){
      this.src = '/default_feature_image.png';
    }
});

I use lazy load and have to do this in order to make it work properly:

lazyload();

var errorURL = "https://example.com/thisimageexist.png";

$(document).ready(function () {
  $('[data-src]').on("error", function () {
    $(this).attr('src', errorURL);
  });
});

I found this to work best, if any image fails to load the first time, it is completely removed from the DOM. Executing console.clear() keeps the console window clean, since the 404 errors cannot be omitted with try/catch blocks.

$('img').one('error', function(err) {
    // console.log(JSON.stringify(err, null, 4))
    $(this).remove()
    console.clear()
})
Related