jQuery/JavaScript to replace broken images
Asked Answered
G

32

589

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.

Gang answered 18/9, 2008 at 13:53 Comment(0)
S
820

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

Sanderlin answered 18/9, 2008 at 14:4 Comment(5)
You probably want to clear out onerror before setting src. Otherwise if noimage.gif is also missing you might end up with a "stack overflow".Combo
I thought & hoped we were moving away from inline attributes for javascript events...Aright
redsquare... they are perfectly understandable when you are judicious about keeping them under control and can even be useful when you want your markup to reflect what is actually going to happen.Pacifa
Good point NickC, about the markup showing what's happening, I just cringe when I look at it :PSumikosumma
@redsquare, I completely agree, but this is a unique case. Inline is the only place where you can attach the listener with absolute certainty that the event will not trigger before your listener is attached. If you do it at the end of the document or wait for DOM loaded you may miss the error event on some images. It does you no good to attach an error handler after the event has fired.Sanderlin
W
214

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");
});
Walliw answered 3/10, 2008 at 19:18 Comment(1)
If you use this technique you can use the "one" method to avoid needing to unbind the event: $('img').one('error', function() { this.src = 'broken.gif'; });Sanderlin
M
136

In case someone like me, tries to attach the error event to a dynamic HTML img tag, I'd like to point out that, there is a catch:

Apparently img error events don't bubble in most browsers, contrary to what the standard says.

So, something like the following will not work:

$(document).on('error', 'img', function () { ... })

Hope this will be helpful to someone else. I wish I had seen this here in this thread. But, I didn't. So, I am adding it

Mindy answered 15/6, 2012 at 13:54 Comment(1)
didn't work. I load a dead image dynamically and append it to the dom, and no 'error' event is fired.Guttle
H
65

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';
    }
  });
});
Hausfrau answered 18/9, 2008 at 14:26 Comment(1)
Almost correct... a correct image in IE will still return (typeof(this.naturalWidth) == "undefined") == true; - I changed the if statement to (!this.complete || (!$.browser.msie && (typeof this.naturalWidth == "undefined" || this.naturalWidth == 0)))Soviet
S
39

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'
});
Shawnna answered 18/9, 2008 at 14:5 Comment(1)
Yes...this is linked in the answer on the first line.Shawnna
S
31
$(window).bind('load', function() {
  $('img').each(function() {
    if( (typeof this.naturalWidth != "undefined" && this.naturalWidth == 0) 
    ||  this.readyState == 'uninitialized'                                  ) {
        $(this).attr('src', 'missing.jpg');
    }
  });
});

Source: http://www.developria.com/2009/03/jquery-quickie---broken-images.html

Stint answered 25/3, 2010 at 23:57 Comment(0)
C
27

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.

Crowl answered 26/10, 2016 at 13:41 Comment(0)
D
11

Here is a quick-and-dirty way to replace all the broken images, and there is no need to change the HTML code ;)

codepen example

    $("img").each(function(){
        var img = $(this);
        var image = new Image();
        image.src = $(img).attr("src");
        var no_image = "https://dummyimage.com/100x100/7080b5/000000&text=No+image";
        if (image.naturalWidth == 0 || image.readyState == 'uninitialized'){
            $(img).unbind("error").attr("src", no_image).css({
                height: $(img).css("height"),
                width: $(img).css("width"),
            });
        }
  });
Demetriusdemeyer answered 18/2, 2014 at 15:59 Comment(0)
N
11

This is a crappy technique, but it's pretty much guaranteed:

<img onerror="this.parentNode.removeChild(this);">
Nasion answered 11/4, 2014 at 13:54 Comment(0)
Y
9

I couldn't find a script to suit my needs, so I made a recursive function to check for broken images and attempt to reload them every four seconds until they are fixed.

I limited it to 10 attempts as if it's not loaded by then the image might not be present on server and the function would enter an infinite loop. I am still testing though. Feel free to tweak it :)

var retries = 0;
$.imgReload = function() {
    var loaded = 1;

    $("img").each(function() {
        if (!this.complete || typeof this.naturalWidth == "undefined" || this.naturalWidth == 0) {

            var src = $(this).attr("src");
            var date = new Date();
            $(this).attr("src", src + "?v=" + date.getTime()); //slightly change url to prevent loading from cache
            loaded =0;
        }
    });

    retries +=1;
    if (retries < 10) { // If after 10 retries error images are not fixed maybe because they
                        // are not present on server, the recursion will break the loop
        if (loaded == 0) {
            setTimeout('$.imgReload()',4000); // I think 4 seconds is enough to load a small image (<50k) from a slow server
        }
        // All images have been loaded
        else {
            // alert("images loaded");
        }
    }
    // If error images cannot be loaded  after 10 retries
    else {
        // alert("recursion exceeded");
    }
}

jQuery(document).ready(function() {
    setTimeout('$.imgReload()',5000);
});
Yacov answered 20/12, 2011 at 12:15 Comment(0)
M
6

This has been frustrating me for years. My CSS fix sets a background image on the img. When a dynamic image src doesn't load to the foreground, a placeholder is visible on the img's bg. This works if your images have a default size (e.g. height, min-height, width and/or min-width).

You'll see the broken image icon but it's an improvement. Tested down to IE9 successfully. iOS Safari and Chrome don't even show a broken icon.

.dynamicContainer img {
  background: url('/images/placeholder.png');
  background-size: contain;
}

Add a little animation to give src time to load without a background flicker. Chrome fades in the background smoothly but desktop Safari doesn't.

.dynamicContainer img {
  background: url('/images/placeholder.png');
  background-size: contain;
  animation: fadein 1s;                     
}

@keyframes fadein {
  0%   { opacity: 0.0; }
  50%  { opacity: 0.5; }
  100% { opacity: 1.0; }
}

.dynamicContainer img {
  background: url('https://picsum.photos/id/237/200');
  background-size: contain;
  animation: fadein 1s;
}

@keyframes fadein {
  0% {
    opacity: 0.0;
  }
  50% {
    opacity: 0.5;
  }
  100% {
    opacity: 1.0;
  }
}

img {
  /* must define dimensions */
  width: 200px;
  height: 200px;
  min-width: 200px;
  min-height: 200px;
  /* hides broken text */
  color: transparent;
  /* optional css below here */
  display: block;
  border: .2em solid black;
  border-radius: 1em;
  margin: 1em;
}
<div class="dynamicContainer">
  <img src="https://picsum.photos/200" alt="Found image" />
  <img src="https://picsumx.photos/200" alt="Not found image" />
</div>
Makeweight answered 25/4, 2014 at 15:20 Comment(0)
M
6

This is JavaScript, should be cross browser compatible, and delivers without the ugly markup onerror="":

var sPathToDefaultImg = 'http://cdn.sstatic.net/stackexchange/img/logos/so/so-icon.png',
    validateImage = function( domImg ) {
        oImg = new Image();
        oImg.onerror = function() {
            domImg.src = sPathToDefaultImg;
        };
        oImg.src = domImg.src;
    },
    aImg = document.getElementsByTagName( 'IMG' ),
    i = aImg.length;

while ( i-- ) {
    validateImage( aImg[i] );
}

CODEPEN:

Montague answered 19/8, 2014 at 6:53 Comment(0)
D
6

You can use GitHub's own fetch for this:

Frontend: https://github.com/github/fetch
or for Backend, a Node.js version: https://github.com/bitinn/node-fetch

fetch(url)
  .then(function(res) {
    if (res.status == '200') {
      return image;
    } else {
      return placeholder;
    }
  }

Edit: This method is going to replace XHR and supposedly already has been in Chrome. To anyone reading this in the future, you may not need the aforementioned library included.

Dugger answered 2/5, 2016 at 19:54 Comment(0)
F
5

If you have inserted your img with innerHTML, like: $("div").innerHTML = <img src="wrong-uri">, you can load another image if it fails doing, e.g, this:

<script>
    function imgError(img) {
        img.error="";
        img.src="valid-uri";
    }
</script>

<img src="wrong-uri" onerror="javascript:imgError(this)">

Why is javascript: _needed? Because scripts injected into the DOM via script tags in innerHTML are not run at the time they are injected, so you have to be explicit.

Foreyard answered 12/11, 2014 at 12:22 Comment(0)
Y
4

Better call using

jQuery(window).load(function(){
    $.imgReload();
});

Because using document.ready doesn't necessary imply that images are loaded, only the HTML. Thus, there is no need for a delayed call.

Yacov answered 23/12, 2011 at 23:4 Comment(0)
A
4
(window.jQuery || window.Zepto).fn.fallback = function (fallback) {
  return this.one('error', function () {
    var self = this;
    this.src = (fallback || 'http://lorempixel.com/$width/$height').replace(
      /\$(\w+)/g, function (m, t) { return self[t] || ''; }
    );
  });
};
    

You can pass a placeholder path and acces in it all properties from the failed image object via $*:

$('img').fallback('http://dummyimage.com/$widthx$height&text=$src');

http://jsfiddle.net/ARTsinn/Cu4Zn/

Allay answered 27/6, 2013 at 20:34 Comment(0)
R
4

CoffeeScript variant:

I made it to fix an issue with Turbolinks that causes the .error() method to get raised in Firefox sometimes even though the image is really there.

$("img").error ->
  e = $(@).get 0
  $(@).hide() if !$.browser.msie && (typeof this.naturalWidth == "undefined" || this.naturalWidth == 0)
Reexamine answered 18/7, 2013 at 17:24 Comment(0)
A
4

By using Prestaul's answer, I added some checks and I prefer to use the jQuery way.

<img src="image1.png" onerror="imgError(this,1);"/>
<img src="image2.png" onerror="imgError(this,2);"/>

function imgError(image, type) {
    if (typeof jQuery !== 'undefined') {
       var imgWidth=$(image).attr("width");
       var imgHeight=$(image).attr("height");

        // Type 1 puts a placeholder image
        // Type 2 hides img tag
        if (type == 1) {
            if (typeof imgWidth !== 'undefined' && typeof imgHeight !== 'undefined') {
                $(image).attr("src", "http://lorempixel.com/" + imgWidth + "/" + imgHeight + "/");
            } else {
               $(image).attr("src", "http://lorempixel.com/200/200/");
            }
        } else if (type == 2) {
            $(image).hide();
        }
    }
    return true;
}
Anoxemia answered 29/6, 2014 at 10:28 Comment(0)
K
4

I found this post while looking at this other SO post. Below is a copy of the answer I gave there.

I know this is an old thread, but React has become popular and, perhaps, someone using React will come here looking for an answer to the same problem.

So, if you are using React, you can do something like the below, which was an answer original provided by Ben Alpert of the React team here

getInitialState: function(event) {
    return {image: "http://example.com/primary_image.jpg"};
},
handleError: function(event) {
    this.setState({image: "http://example.com/failover_image.jpg"});
},
render: function() {
    return (
        <img onError={this.handleError} src={src} />;
    );
}
Knockknee answered 13/7, 2016 at 18:28 Comment(0)
B
4

I created a fiddle to replace the broken image using "onerror" event. This may help you.

    //the placeholder image url
    var defaultUrl = "url('https://sadasd/image02.png')";

    $('div').each(function(index, item) {
      var currentUrl = $(item).css("background-image").replace(/^url\(['"](.+)['"]\)/, '$1');
      $('<img>', {
        src: currentUrl
      }).on("error", function(e) {
        $this = $(this);
        $this.css({
          "background-image": defaultUrl
        })
        e.target.remove()
      }.bind(this))
    })
Borate answered 1/9, 2016 at 13:23 Comment(0)
H
4

Here is an example using the HTML5 Image object wrapped by JQuery. Call the load function for the primary image URL and if that load causes an error, replace the src attribute of the image with a backup URL.

function loadImageUseBackupUrlOnError(imgId, primaryUrl, backupUrl) {
    var $img = $('#' + imgId);
    $(new Image()).load().error(function() {
        $img.attr('src', backupUrl);
    }).attr('src', primaryUrl)
}

<img id="myImage" src="primary-image-url"/>
<script>
    loadImageUseBackupUrlOnError('myImage','primary-image-url','backup-image-url');
</script>
Hundred answered 4/11, 2016 at 16:0 Comment(0)
B
4

Pure JS. My task was: if image 'bl-once.png' is empty -> insert the first one (that hasn't 404 status) image from array list (in current dir):

<img src="http://localhost:63342/GetImage/bl-once.png" width="200" onerror="replaceEmptyImage.insertImg(this)">

Maybe it needs to be improved, but:

var srcToInsertArr = ['empty1.png', 'empty2.png', 'needed.png', 'notActual.png']; // try to insert one by one img from this array
    var path;
    var imgNotFounded = true; // to mark when success

    var replaceEmptyImage = {
        insertImg: function (elem) {

            if (srcToInsertArr.length == 0) { // if there are no more src to try return
                return "no-image.png";
            }
            if(!/undefined/.test(elem.src)) { // remember path
                path = elem.src.split("/").slice(0, -1).join("/"); // "http://localhost:63342/GetImage"
            }
            var url = path + "/" + srcToInsertArr[0];

            srcToInsertArr.splice(0, 1); // tried 1 src

            
                if(imgNotFounded){ // while not success
                    replaceEmptyImage.getImg(url, path, elem); // CALL GET IMAGE
                }
            

        },
        getImg: function (src, path, elem) { // GET IMAGE

            if (src && path && elem) { // src = "http://localhost:63342/GetImage/needed.png"
                
                var pathArr = src.split("/"); // ["http:", "", "localhost:63342", "GetImage", "needed.png"]
                var name = pathArr[pathArr.length - 1]; // "needed.png"

                xhr = new XMLHttpRequest();
                xhr.open('GET', src, true);
                xhr.send();

                xhr.onreadystatechange = function () {

                    if (xhr.status == 200) {
                        elem.src = src; // insert correct src
                        imgNotFounded = false; // mark success
                    }
                    else {
                        console.log(name + " doesn't exist!");
                        elem.onerror();
                    }

                }
            }
        }

    };

So, it will insert correct 'needed.png' to my src or 'no-image.png' from current dir.

Beamer answered 7/4, 2017 at 15:10 Comment(0)
T
4

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;
    }}
 />
Tipi answered 1/10, 2021 at 4:49 Comment(0)
U
3

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.

Umber answered 18/9, 2008 at 14:5 Comment(0)
T
3

I solved my problem with these two simple functions:

function imgExists(imgPath) {
    var http = jQuery.ajax({
                   type:"HEAD",
                   url: imgPath,
                   async: false
               });
    return http.status != 404;
}

function handleImageError() {
    var imgPath;

    $('img').each(function() {
        imgPath = $(this).attr('src');
        if (!imgExists(imgPath)) {
            $(this).attr('src', 'images/noimage.jpg');
        }
    });
}
Timikatiming answered 21/10, 2013 at 11:11 Comment(0)
P
3

jQuery 1.8

// If missing.png is missing, it is replaced by replacement.png
$( "img" )
  .error(function() {
    $( this ).attr( "src", "replacement.png" );
  })
  .attr( "src", "missing.png" );

jQuery 3

// If missing.png is missing, it is replaced by replacement.png
$( "img" )
  .on("error", function() {
    $( this ).attr( "src", "replacement.png" );
  })
  .attr( "src", "missing.png" );

reference

Persecution answered 2/9, 2017 at 3:16 Comment(0)
I
3

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;
  }
}
Illimani answered 14/1, 2018 at 15:56 Comment(0)
R
3

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->,,,,,

Recessional answered 23/5, 2018 at 9:37 Comment(2)
This is by far the best anwer. Only thing I would suggest is to use let isImgErrorHandled = target.src === 'backup.png'; as it simplifies a bit.Excursionist
@Sabo, There is a little problem because src path may not equal the path I assigned. For example, target.src='backup.png', but next time console.log(target.src) may not be backup.pngRecessional
H
2

If the image cannot be loaded (for example, because it is not present at the supplied URL), image URL will be changed into default,

For more about .error()

$('img').on('error', function (e) {
  $(this).attr('src', 'broken.png');
});
Huxley answered 20/2, 2018 at 4:41 Comment(0)
N
1

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';
    }
});
Naos answered 16/11, 2017 at 20:43 Comment(0)
M
1

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);
  });
});
Matchless answered 12/3, 2020 at 13:2 Comment(0)
A
0

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()
})
Advisory answered 1/2, 2019 at 0:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.