260

I'm using JavaScript with the jQuery library to manipulate image thumbnails contained in a unordered list. When the image is loaded it does one thing, when an error occurs it does something else. I'm using jQuery load() and error() methods as events. After these events I check the image DOM element for the .complete to make sure the image wasn't already loaded before jQuery could register the events.

It works correctly except when an error occurs before jQuery can register the events. The only solution I can think of is to use the img onerror attribute to store a "flag" somewhere globally (or on the node it's self) that says it failed so jQuery can check that "store/node" when checking .complete.

Anyone have a better solution?

Edit: Bolded main points and added extra detail below: I'm checking if an image is complete (aka loaded) AFTER I add a load and error event on the image. That way, if the image was loaded before the events were registered, I will still know. If the image isn't loaded after the events then the events will take care of it when it does. The problem with this is, I can easily check if an image is loaded already, but I can't tell if an error occurred instead.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
William
  • 15,465
  • 8
  • 36
  • 32
  • When do you register the jQuery events? Maybe some codes will help. :) – o.k.w Dec 30 '09 at 01:02
  • Theres a lot of code, what I said above is just a very simple version of what I'm doing. I call the events after the DOM is loaded a method is called to add events to the thumbnails. – William Dec 30 '09 at 01:06
  • 1
    possible duplicate of [How can I determine if an image has loaded, using Javascript/jQuery?](http://stackoverflow.com/questions/263359/how-can-i-determine-if-an-image-has-loaded-using-javascript-jquery) – Steve Tauber Jun 28 '13 at 22:35

15 Answers15

282

Check the complete and naturalWidth properties, in that order.

https://stereochro.me/ideas/detecting-broken-images-js

function IsImageOk(img) {
    // During the onload event, IE correctly identifies any images that
    // weren’t downloaded as not complete. Others should too. Gecko-based
    // browsers act like NS4 in that they report this incorrectly.
    if (!img.complete) {
        return false;
    }

    // However, they do have two very useful properties: naturalWidth and
    // naturalHeight. These give the true size of the image. If it failed
    // to load, either of these should be zero.
    if (img.naturalWidth === 0) {
        return false;
    }

    // No other way of checking: assume it’s ok.
    return true;
}
aleclarson
  • 18,087
  • 14
  • 64
  • 91
SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • 1
    Interesting, I was looking at that post earlier and I didn't realize that they were doing ! complete for IE as naturalWidth is not defined in IE. Going to check it now. – William Dec 30 '09 at 01:07
  • I think I should have explained better in my main question. I'm calling this after I add the events for load,error so that if it already had an error I will know. This method returns false even if a method is still "loading" and it has 0 errors. I need something to check if it has had an error, if it's still loading / complete then it should return true. Thanks for the answer though – William Dec 30 '09 at 01:30
  • Updated, let me know if you want anymore details. Thanks for the help – William Dec 30 '09 at 01:51
  • 3
    Apparently, this only works reliably the first time. If you then change the src of the image, at least safari mobile will keep reporting the first value for both naturalWidth and complete. – giorgian Apr 23 '13 at 09:20
  • 6
    return img.complete && typeof img.naturalWidth != 'undefined' && img.naturalWidth != 0; – Adrian Seeley Jan 19 '14 at 13:08
  • so what now? hammer the CPU with an interval, nagging it to check if the image has been loaded already? maybe it's dead and out interval will continue for a long time? – vsync Mar 29 '14 at 21:36
  • 5
    @vsync you would probably want to use this as a one-time check, and if the image has not yet loaded, set up a "load" event handler. There shouldn't be any need to hammer the CPU by running this check multiple times. – Michael Martin-Smucker Apr 15 '14 at 15:03
  • @SLaks First thing to note: `naturalWidth` & `naturalHeight` are "only" IE9+ compatible, see this for cross-browser support http://stackoverflow.com/a/7869044/759452. Second thing to note: your answer is similar to this answer http://stackoverflow.com/a/3016076/759452 inspired from this article from StereoChrome written in 2005 https://stereochro.me/ideas/detecting-broken-images-js – Adriano Oct 13 '14 at 13:07
  • @MichaelMartin-Smucker seems like your last comment ("you would probably want to use this as a one-time check, and if the image has not yet loaded, set up a "load" event handler.") combines the best of both worlds: allowing to lower the amount of handlers attached, but also probably solving the issue with cached images not triggering the `.load()` handler. Maybe something like this would work? http://jsbin.com/qahuqatiyomo/4/edit?js,console,output – Adriano Oct 13 '14 at 13:37
  • 3
    For those curious, this is pretty much exactly what the imagesLoaded library linked below does. So, for a one-off usage, go for it: https://github.com/desandro/imagesloaded/blob/master/imagesloaded.js#L284-L288 – cincodenada Nov 13 '18 at 00:27
  • Doesn't work for SVG, `naturalHeight` and `naturalWidth` returns 0 – cdalxndr Apr 24 '20 at 07:28
272

Another option is to trigger the onload and/or onerror events by creating an in memory image element and setting its src attribute to the original src attribute of the original image. Here's an example of what I mean:

$("<img/>")
    .on('load', function() { console.log("image loaded correctly"); })
    .on('error', function() { console.log("error loading image"); })
    .attr("src", $(originalImage).attr("src"))
;
starball
  • 20,030
  • 7
  • 43
  • 238
Xavi
  • 20,111
  • 14
  • 72
  • 63
  • Hmm that seems to work, and after a few tests it doesn't seem to hurt the loading of the image either. Thanks! – William Dec 30 '09 at 02:10
  • 30
    Doesn't seem to be working as well as I hoped. In Google Chrome it seems to be having issues when the image is already in cache. It doesn't trigger the load() method. :( – William Dec 30 '09 at 03:05
  • 4
    Ugg, you're right. Chrome is definitely the most annoying browser to develop for. On the bright, I think I may have found a work around: set the image source to "" then back to the original source. I'll update my answer. – Xavi Dec 30 '09 at 03:25
  • Looks like we were both thinking the same thing. It seems to work so far. I'll write back if I find another bug but so far it's working good. :) – William Dec 30 '09 at 03:28
  • Doesn't play well with IE6. It likes to cause an error event when changing the src. Also, in IE6 when you change the src it creates a new HTTP request. :( – William Dec 31 '09 at 21:23
  • I've updated the answer with a solution that works in all browsers and avoids unnecessary HTTP requests. – Xavi Dec 24 '11 at 11:17
  • 1
    You should put the `.attr` line *after* the `.load` and `.error` lines. Otherwise you risk that the image might load (or encounter an error) *before* those callbacks are added, and they won't be called. – callum Feb 07 '12 at 11:32
  • @William you're right, load() might not trigger if the image is in cache. One solution is to append a unique query string to the URI, like '?' + Math.random(). Browsers should then ignore the local cached version. – Romain Feb 10 '12 at 05:40
  • 20
    @Xavi Chrome is not the most annoying browser to develop for, try developing for Internet Explorer 7 or less. Besides adding a $_GET parameter to the image load, will load a new image everytime, like Gromix suggested. – SSH This Jan 14 '13 at 23:51
  • 11
    The `.load()` and `.error()` methods are confusing and now deprecated, use `.on()` and use the `load` and `error` as events. – Firsh - justifiedgrid.com Mar 08 '14 at 20:12
  • 2
    the answer below is better. check for `complete` and `naturalWidth`, otherwise you're just using extra bandwidth for something you don't need to – dansch Feb 23 '15 at 19:12
  • 1
    The issue with the `IsImageOk` is that when false is returned, it's unclear whether an error occurred or if the image simply hasn't finished loading. So you're be force to create a busy loop calling `IsImageOk` over and over again. As for bandwidth usage, when you create the in-memory version of an image, it's very likely that the image data will be pulled from the browser's cache. In my experience, only older versions of IE issue another GET request. – Xavi Feb 23 '15 at 23:37
73

Based on my understanding of the W3C HTML Specification for the img element, you should be able to do this using a combination of the complete and naturalHeight attributes, like so:

function imgLoaded(imgElement) {
  return imgElement.complete && imgElement.naturalHeight !== 0;
}

From the spec for the complete attribute:

The IDL attribute complete must return true if any of the following conditions is true:

  • The src attribute is omitted.
  • The final task that is queued by the networking task source once the resource has been fetched has been queued.
  • The img element is completely available.
  • The img element is broken.

Otherwise, the attribute must return false.

So essentially, complete returns true if the image has either finished loading, or failed to load. Since we want only the case where the image successfully loaded we need to check the nauturalHeight attribute as well:

The IDL attributes naturalWidth and naturalHeight must return the intrinsic width and height of the image, in CSS pixels, if the image is available, or else 0.

And available is defined like so:

An img is always in one of the following states:

  • Unavailable - The user agent hasn't obtained any image data.
  • Partially available - The user agent has obtained some of the image data.
  • Completely available - The user agent has obtained all of the image data and at least the image dimensions are available.
  • Broken - The user agent has obtained all of the image data that it can, but it cannot even decode the image enough to get the image dimensions (e.g. the image is corrupted, or the format is not supported, or no data could be obtained).

When an img element is either in the partially available state or in the completely available state, it is said to be available.

So if the image is "broken" (failed to load), then it will be in the broken state, not the available state, so naturalHeight will be 0.

Therefore, checking imgElement.complete && imgElement.naturalHeight !== 0 should tell us whether the image has successfully loaded.

You can read more about this in the W3C HTML Specification for the img element, or on MDN.

Ajedi32
  • 45,670
  • 22
  • 127
  • 172
  • 1
    Doesn't work is image is loaded with style "content: url" – deathangel908 Apr 30 '17 at 21:12
  • 2
    This doesn't seem to work in Firefox with SVG images as the browser reports natural height/width to be 0. Relevant bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1328124 – leeb Sep 11 '19 at 11:40
26

I tried many different ways and this way is the only one worked for me

//check all images on the page
$('img').each(function(){
    var img = new Image();
    img.onload = function() {
        console.log($(this).attr('src') + ' - done!');
    }
    img.src = $(this).attr('src');
});

You could also add a callback function triggered once all images are loaded in the DOM and ready. This applies for dynamically added images too. http://jsfiddle.net/kalmarsh80/nrAPk/

Kal
  • 948
  • 17
  • 30
  • 1
    I've tested the interesting reads above and they fail between cached/uncached tests on a handful of common browsers. This answer is similar to another but is the one I implemented & tested just now, I can confirm it works on: EdgeWin10, IE11Win8.1, Win7IE10, Win7IE9, iOS 10.2 Chrome, iOS 10.2 Safari, mac os 10.12 Chrome, mac os 10.12 Safari, mac os 10.12 Firefox, Google Pixel 7.1, Samsung S5 Android 4.4. Don't forget to use addEventListener/removeEventListener too :D – danjah Dec 09 '16 at 03:00
  • Note that this technically loads *another* copy of the image and uses load event of that proxy as event for the image. This obviously fails to match the original image with the same URL if HTTP headers do not allow the cached result to be shared! – Mikko Rantalainen Nov 25 '21 at 12:02
14

Use imagesLoaded javascript library.

Usable with plain Javascript and as a jQuery plugin.

Features:

Resources

Community
  • 1
  • 1
Josh Harrison
  • 5,927
  • 1
  • 30
  • 44
  • This worked perfectly for me. function checkImages(imgs) { $(imgs).each(function() { $(imgs).imagesLoaded() .progress(function(instance, image) { if(image.isLoaded == false) { console.log(image.img.src + ' could not be found.'); image.img.src = "/templates/img/no-image.jpg"; } }); }); } – Rooster242 Mar 11 '14 at 18:23
  • 2
    it seems to have TON of open issues which major bugs, and the developer's doesn't seem to do much catching up with them. as of now, this is totally unusable in a production environment. – vsync Mar 29 '14 at 22:07
  • 3
    @vsync Have you read some of the open issues? He responds to nearly all of them, and most seem to be unreproducible edge-cases which few others encounter. I've never had a problem with it in production. – Josh Harrison Apr 23 '14 at 11:43
  • 1
    yes I use it myself and sometimes it doesn't work, so I use a setTimeout to cover cases where it fails – vsync Apr 23 '14 at 15:01
  • There are good reasons WHY one want to use the imagesLoaded plugin: `complete` property does not seem to be clearly supported: unable to find a reliable source of info regarding browser support. `complete` property also doesn't seem to have clear specification: the HTML5 spec is the only one mentioning it & it's still in a draft version at the time of writting. If you use `naturalWidth` & `naturalHeight` only have IE9+ support, so if you use it to find out if the image is loaded, you need some "clever" trick to make it work on old browsers, & you must check cross-browser behavior is consistent – Adriano Oct 15 '14 at 09:23
  • Backing up my previous comment: http://www.w3.org/html/wg/drafts/html/master/embedded-content.html#dom-img-complete - the only spec regarding the "complete" property. http://www.w3.org/TR/html401/struct/objects.html#h-13.2 - HTML4 spec : nothing mentioned about "complete" property. https://developer.mozilla.org/en/docs/Web/API/HTMLImageElement - bottom table shows support for the "complete" property as unknown (question mark). Finally, regarding `naturalWidth` & `naturalHeight` browser support http://stackoverflow.com/questions/6900177/how-to-check-if-naturalwidth-is-supported – Adriano Oct 27 '14 at 12:14
  • Even imagesLoaded seems to fail for the situation where an image returns a 404 *and* content, e.g. YouTube's thumbnails where the video is valid but no such thumbnail exists. – El Yobo Oct 13 '15 at 01:37
  • That library should be used *only* if you need to support old user agents that do not support property `.complete` instead. That library loads *extra* copy of the image and assumes that load event for that copy is good signal for the original image, too. – Mikko Rantalainen Nov 25 '21 at 12:03
  • @MikkoRantalainen yes that's very true for today. This answer was from 8 years ago! Today, checking to see if the `complete` property is `true`, and if not, using a `load` event listener, should be enough. – Josh Harrison Nov 25 '21 at 16:24
10

Retrieve informations from image elements on the page
Test working on Chrome and Firefox
Working jsFiddle (open your console to see the result)

$('img').each(function(){ // selecting all image element on the page

    var img = new Image($(this)); // creating image element

    img.onload = function() { // trigger if the image was loaded
        console.log($(this).attr('src') + ' - done!');
    }

    img.onerror = function() { // trigger if the image wasn't loaded
        console.log($(this).attr('src') + ' - error!');
    }

    img.onAbort = function() { // trigger if the image load was abort
        console.log($(this).attr('src') + ' - abort!');
    }

    img.src = $(this).attr('src'); // pass src to image object

    // log image attributes
    console.log(img.src);
    console.log(img.width);
    console.log(img.height);
    console.log(img.complete);

});

Note : I used jQuery, I thought this can be acheive on full javascript

I find good information here OpenClassRoom --> this is a French forum

Julha
  • 1,107
  • 14
  • 14
  • Note that this version also loads *extra* copy for each image and if caching doesn't allow using the same copy you just double the download size and get wrong event timing. – Mikko Rantalainen Nov 25 '21 at 12:04
5

After reading the interesting solutions on this page, I created an easy-to-use solution highly influenced by SLaks' and Noyo's post that seems to be working on pretty recent versions (as of writing) of Chrome, IE, Firefox, Safari, and Opera (all on Windows). Also, it worked on an iPhone/iPad emulator I used.

One major difference between this solution and SLaks and Noyo's post is that this solution mainly checks the naturalWidth and naturalHeight properties. I've found that in the current browser versions, those two properties seem to provide the most helpful and consistent results.

This code returns TRUE when an image has loaded fully AND successfully. It returns FALSE when an image either has not loaded fully yet OR has failed to load.

One thing you will need to be aware of is that this function will also return FALSE if the image is a 0x0 pixel image. But those images are quite uncommon, and I can't think of a very useful case where you would want to check to see if a 0x0 pixel image has loaded yet :)

First we attach a new function called "isLoaded" to the HTMLImageElement prototype, so that the function can be used on any image element.

HTMLImageElement.prototype.isLoaded = function() {

    // See if "naturalWidth" and "naturalHeight" properties are available.
    if (typeof this.naturalWidth == 'number' && typeof this.naturalHeight == 'number')
        return !(this.naturalWidth == 0 && this.naturalHeight == 0);

    // See if "complete" property is available.
    else if (typeof this.complete == 'boolean')
        return this.complete;

    // Fallback behavior: return TRUE.
    else
        return true;

};

Then, any time we need to check the loading status of the image, we just call the "isLoaded" function.

if (someImgElement.isLoaded()) {
    // YAY! The image loaded
}
else {
    // Image has not loaded yet
}

Per giorgian's comment on SLaks' and Noyo's post, this solution probably can only be used as a one-time check on Safari Mobile if you plan on changing the SRC attribute. But you can work around that by creating an image element with a new SRC attribute instead of changing the SRC attribute on an existing image element.

data-dan
  • 76
  • 1
  • 1
  • Note that some browsers return naturalWidth==0 for all SVG images to signal that those can be scaled. You should always prefer checking `.complete` first and resorting to hacks only if that cannot be used. – Mikko Rantalainen Nov 25 '21 at 12:05
4

Realtime network detector - check network status without refreshing the page: (it's not jquery, but tested, and 100% works:(tested on Firefox v25.0))

Code:

<script>
 function ImgLoad(myobj){
   var randomNum = Math.round(Math.random() * 10000);
   var oImg=new Image;
   oImg.src="YOUR_IMAGELINK"+"?rand="+randomNum;
   oImg.onload=function(){alert('Image succesfully loaded!')}
   oImg.onerror=function(){alert('No network connection or image is not available.')}
}
window.onload=ImgLoad();
</script>

<button id="reloadbtn" onclick="ImgLoad();">Again!</button>

if connection lost just press the Again button.

Update 1: Auto detect without refreshing the page:

<script>
     function ImgLoad(myobj){
       var randomNum = Math.round(Math.random() * 10000);
       var oImg=new Image;
       oImg.src="YOUR_IMAGELINK"+"?rand="+randomNum;
       oImg.onload=function(){networkstatus_div.innerHTML="";}
       oImg.onerror=function(){networkstatus_div.innerHTML="Service is not available. Please check your Internet connection!";}
}

networkchecker = window.setInterval(function(){window.onload=ImgLoad()},1000);
</script>

<div id="networkstatus_div"></div>
  • 100%! What if your image link is blocked by a proxy/firewall or the image server not responding? You still have the network working :) – Sen Jacob Dec 04 '14 at 12:33
  • This will load every image once again from the server, busting the cache (intentionally). So just for checking, you're (at least) doubling bandwidth for all of your images. Besides, it checks only if image exists on the server, not whether it was loaded in some specific place. Also no way to know when each image is loaded. – Marius Balčytis Nov 22 '15 at 17:03
2

This is how I got it to work cross browser using a combination of the methods above (I also needed to insert images dynamically into the dom):

$('#domTarget').html('<img src="" />');

var url = '/some/image/path.png';

$('#domTarget img').load(function(){}).attr('src', url).error(function() {
    if ( isIE ) {
       var thisImg = this;
       setTimeout(function() {
          if ( ! thisImg.complete ) {
             $(thisImg).attr('src', '/web/css/img/picture-broken-url.png');
          }
       },250);
    } else {
       $(this).attr('src', '/web/css/img/picture-broken-url.png');
    }
});

Note: You will need to supply a valid boolean state for the isIE variable.

Justin Vincent
  • 1,329
  • 2
  • 14
  • 22
2
var isImgLoaded = function(imgSelector){
  return $(imgSelector).prop("complete") && $(imgSelector).prop("naturalWidth") !== 0;
}

// Or As a Plugin

    $.fn.extend({
      isLoaded: function(){
        return this.prop("complete") && this.prop("naturalWidth") !== 0;
      }
    })

// $(".myImage").isLoaded() 
boubkhaled
  • 379
  • 5
  • 6
1

This snippet of code helped me to fix browser caching problems:

$("#my_image").on('load', function() {

    console.log("image loaded correctly"); 
}).each(function() {

     if($(this).prop('complete')) $(this).load();
});

When the browser cache is disabled, only this code doesn't work:

$("#my_image").on('load', function() {
     console.log("image loaded correctly"); 
})

to make it work you have to add:

.each(function() {
     if($(this).prop('complete')) $(this).load();
});
Frank
  • 809
  • 1
  • 10
  • 12
1

As I understand the .complete property is non-standard. It may not be universal... I notice it seem to work differently in Firefox verses IE. I am loading a number of images in javascript then checking if complete. In Firefox, this seems to work great. In IE, it doesn't because the images appear to be loading on another thread. It works only if I put a delay between my assignment to image.src and when I check the image.complete property.

Using image.onload and image.onerror isn't working for me, either, because I need to pass a parameter to know which image I am talking about when the function is called. Any way of doing that seems to fail because it actually seems to pass the same function, not different instances of the same function. So the value I pass into it to identify the image always ends up being the last value in the loop. I cannot think of any way around this problem.

On Safari and Chrome, I am seeing the image.complete true and the naturalWidth set even when the error console shows a 404 for that image... and I intentionally removed that image to test this. But the above works well for Firefox and IE.

  • Hmm, that's interesting. Would mind posting a code example so I want try a couple of things out? – Xavi Dec 24 '11 at 11:21
0

Using this JavaScript code you can check image is successfully loaded or not.

document.onready = function(e) {
        var imageobj = new Image();
        imageobj.src = document.getElementById('img-id').src;
        if(!imageobj.complete){ 
            alert(imageobj.src+"  -  Not Found");
        }
}

Try out this

Ajay Gupta
  • 2,867
  • 23
  • 28
0

I had a lot of problems with the complete load of a image and the EventListener.

Whatever I tried, the results was not reliable.

But then I found the solution. It is technically not a nice one, but now I never had a failed image load.

What I did:

                    document.getElementById(currentImgID).addEventListener("load", loadListener1);
                    document.getElementById(currentImgID).addEventListener("load", loadListener2);

                function loadListener1()
                    {
                    // Load again
                    }

                function loadListener2()
                {
                    var btn = document.getElementById("addForm_WithImage"); btn.disabled = false;
                    alert("Image loaded");
                }

Instead of loading the image one time, I just load it a second time direct after the first time and both run trough the eventhandler.

All my headaches are gone!


By the way: You guys from stackoverflow helped me already more then hundred times. For this a very big Thank you!

0

This one worked fine for me :)

$('.progress-image').each(function(){
    var img = new Image();
    img.onload = function() {
        let imgSrc = $(this).attr('src');
        $('.progress-image').each(function(){
            if($(this).attr('src') == imgSrc){
                console.log($(this).parent().closest('.real-pack').parent().find('.shimmer').fadeOut())
            }
        })
    }
    img.src = $(this).attr('src');
});