55

In IE, you can onreadystatechange. There's onload, but I read scary things. jQuery wraps up the DOM's load event quite nicely with "ready". It seems likely I am just ignorant of another nice library's implementation of image loading.

The context is that I am generating images dynamically (via server callbacks) that can take some time download. In my IE-only code I set the src of the img element, then when the onreadystatechange event fires with the "complete" status, I add it to the DOM so the user sees it.

I'd be happy with a "native" JavaScript solution, or a pointer to a library that does the work. There's so many libraries out there and I'm sure this is a case of me just not knowing about the right one. That said, we're already jQuery users, so I'm not eager to add a very large library just to get this functionality.

Community
  • 1
  • 1
Sebastian Good
  • 6,310
  • 2
  • 33
  • 57
  • I don't fully understand - do you want a single pure JS solution for detecting when an image has loaded, or are you looking for an alternative library to jQuery for detecting when an image has loaded? – Dan Lew May 04 '09 at 19:22
  • I'd be happy with either. I wouldn't ditch jQuery as I use it elsewhere; just expressing my suspicion that this is a well-solved problem in a library I am not familiar with. – Sebastian Good May 04 '09 at 19:26
  • Don't forget to let us know what you end up doing! – Jason Bunting May 04 '09 at 21:45
  • 1
    After tones of reading, the best answer seems to be to use [imagesLoaded Javascript library](https://github.com/desandro/imagesloaded), it is: MIT License, on Github, massive community, mature project (very old, since 2010, but still active), 7Kb minified (light!), & no dependencies. related http://stackoverflow.com/questions/1977871/check-if-an-image-is-loaded-no-errors-in-javascript – Adriano Nov 04 '14 at 09:53

9 Answers9

47

NOTE: I wrote this in 2010, the browsers in the wild were IE 8/9 beta, Firefox 3.x, and Chrome 4.x. Please use this for research purposes only, I doubt you could copy/paste this into a modern browser and have it work without issue.

WARNING: It is 2017 now I still get points on this now and then, please only use this for research purposes. I currently have no idea how to detect image loading status, but there are probably much more graceful ways of doing it than this... at least I seriously hope there are. I highly recommend NOT using my code in a production environment without more research.

WARNING Part Two Electric Boogaloo: It is 2019 now, most jQuery functionality is built into vanilla JS now. If you're still using it, it may be time to stop and consider heading over to MDN and reading up on some of the new and fun stuff vanilla JS has to offer.


I'm a bit late to this party, maybe this answer will help someone else...

If you're using jQuery don't bother with the stock event handlers (onclick/onmouseover/etc), actually just stop using them altogether. Use the event methods they provided in their API.


This will alert, before the image is appended to the body, because load event is triggered when the image is loaded into memory. It is doing exactly what you tell it to: create an image with the src of test.jpg, when test.jpg loads do an alert, then append it to the body.

var img = $('<img src="test.jpg" />');
img.load(function() {
    alert('Image Loaded');
});
$('body').append(img);

This will alert, after the image is inserted into the body, again, doing what you told it to: create an image, set an event (no src set, so it hasn't loaded), append the image to the body (still no src), now set the src... now the image is loaded so the event is triggered.

var img = $('<img />');
img.load(function() {
    alert('Image Loaded');
});
$('body').append(img);
$img.attr('src','test.jpg');

You can of course also add an error handler and merge a bunch of events using bind().

var img = $('<img />');
img.bind({
    load: function() {
        alert('Image loaded.');
    },
    error: function() {
        alert('Error thrown, image didn\'t load, probably a 404.');
    }
});
$('body').append(img);
img.attr('src','test.jpg');

Per the request by @ChrisKempen ...

Here is a non-event driven way of determining if the images are broken after the DOM is loaded. This code is a derivative of code from an article by StereoChrome which uses naturalWidth, naturalHeight, and complete attributes to determine if the image exists.

$('img').each(function() {
    if (this.naturalWidth === 0 || this.naturalHeight === 0 || this.complete === false) {
        alert('broken image');
    }
});
Paul J
  • 1,489
  • 1
  • 17
  • 19
  • 12
    WARNING: From the jquery docs: Caveats of the load event when used with images: A common challenge developers attempt to solve using the .load() shortcut is to execute a function when an image (or collection of images) have completely loaded. There are several known caveats with this that should be noted. These are: It doesn't work consistently nor reliably cross-browser It doesn't fire correctly in WebKit if the image src is set to the same src as before It doesn't correctly bubble up the DOM tree Can cease to fire for images that already live in the browser's cache – Ben Jun 11 '12 at 17:45
  • 1
    I'm even later to the party, but what if the images have already been added and you need to check if they have been loaded? Thank you for any thoughts / suggestions! :) – Chris Kempen Jul 08 '12 at 09:50
  • 5
    @ChrisKempen -- I added a non-event driven method for you in my post above, please also view the original article from which my code derives. – Paul J Jul 20 '12 at 19:39
  • 1
    @paulj note that `naturalWidth` & `naturalHeight` are "only" IE9+ compatible, see this for cross-browser support http://stackoverflow.com/a/7869044/759452 and http://www.jacklmoore.com/notes/naturalwidth-and-naturalheight-in-ie/ , maybe inspired by http://css-tricks.com/snippets/jquery/get-an-images-native-width/ – Adriano Oct 13 '14 at 13:15
11

According to the W3C spec, only the BODY and FRAMESET elements provide an "onload" event to attach to. Some browsers support it regardless, but just be aware that it is not required to implement the W3C spec.

Something that might be pertinent to this discussion, though not necessarily the answer you are in need of, is this discussion:

Image.onload event does not fire on Internet Explorer when image is in cache


Something else that may be related to your needs, though it may not, is this info on supporting a synthesized "onload" event for any dynamically-loaded DOM element:

How can I determine if a dynamically-created DOM element has been added to the DOM?

Community
  • 1
  • 1
Jason Bunting
  • 58,249
  • 14
  • 102
  • 93
  • that might be true, but onload for images is a de-facto standard (it's been availabe since Netscape Navigator 4) – Christoph May 04 '09 at 20:05
  • Sure, I never said that it wasn't something he could count on, simply making sure he is aware of the spec - ignore the specs at your own peril. – Jason Bunting May 04 '09 at 20:14
  • the problem with Image.onload you mention is GWT-specific - see http://code.google.com/p/google-web-toolkit/issues/detail?id=863#c5 ; Image.onload has to be set before Image.src to avoid this, which has already been implicitly mentioned by Josh – Christoph May 04 '09 at 20:32
  • @Christoph - in your myopia you have forgotten two things: 1) I indicated that it might not be "pertinent to this discussion," but more importantly, 2) other people may land on this page some day because they ran into the problem I linked to, and got here because they were searching SO for their specific problem. If I had posted a link to a problem with tuples in erlang, I could see you getting upset, but I feel my comments are relevant. – Jason Bunting May 04 '09 at 20:40
  • @Jason: I'm not upset or anything, but your answer imo suggests that using onload with images is more problematic than it actually is; linking to a GWT-specific problem when discussing vanilla JS just seems a bit odd to me - it's not a bug, but reasonable and documented behaviour - check the remarks section of http://msdn.microsoft.com/en-us/library/cc197055(VS.85).aspx – Christoph May 04 '09 at 20:51
  • OK boys, settle down. Since I'm adding the elements to the DOM myself, I'm not as interested in the additional link. I suppose the implication here is that in IE I should use onreadystatechange, and everywhere else I should use onload which, while not standard, is widely supported? – Sebastian Good May 04 '09 at 20:51
  • @Sebastian - I don't know, at this point I am just having a good time arguing over minutiae with Christoph. :) J/K Personally, unless you really feel strongly about it, I would simply use, if you are already doing so, jQuery to handle things. I didn't quite understand if you were having problems with this solution or just were not sure if you should try it. I would say, try it, test a few browsers with it, and if it works, you are done. Otherwise, just make sure you handle IE differently, but I would keep to jQuery if you are already using it. – Jason Bunting May 04 '09 at 21:08
  • @Sebastian: yep, nothing wrong with a good argument about a non-issue ;) @Jason: "You fight like a dairy farmer!" – Christoph May 04 '09 at 21:13
  • @Christoph: "How appropriate. You fight like a cow." :) – Jason Bunting May 04 '09 at 21:44
6

The only reliable way I've found is to do it all on the client-side like this...

var img = new Image();
img.onload = function() {
  alert('Done!');
}
img.src = '/images/myImage.jpg';
Josh Stodola
  • 81,538
  • 47
  • 180
  • 227
  • 1
    Although a few browsers support it, the "onload" event is not supported by the W3C standard, and thus not all browsers need to or have to support this. – Jason Bunting May 04 '09 at 19:49
3

I think onload should work fine. Do you have to generate the markup for the images, or can you add them statically? If the latter, I'd suggest the following:

<img src="foo.png" class="classNeededToHideImage" onload="makeVisible(this)">

Alternatively, you can use window.onload to make all images visible at once - window.onload fires after all external ressources have finished loading.

If you want to add the images dynamically, you first have to wait for the DOM to be ready to be modified, ie use jQuery or implement your own DOMContentLoaded hack for browsers which don't support it natively.

Then, you create your image objects, assign onload listeners to make them visible and/or add them to the document.

Even better (but slightly more complicated) would be to create and start loading the images immediately when the script executes. On DOMContentLoaded, you'll have to check each image for complete to decide whether to add them immediately to the document or wait for the image's onload listener to fire.

Christoph
  • 164,997
  • 36
  • 182
  • 240
  • 1
    Although a few browsers support it, the "onload" event is not supported by the W3C standard, and thus not all browsers need to or have to support this. – Jason Bunting May 04 '09 at 19:53
  • Well, when 'a few browsers' seems to be every mainstream browser sinde NN4, it's reasonably safe to assume that the feature won't go away any time soon. Sad as it may be, the W3C specs seldom reflect the reality web developers face... – Christoph May 04 '09 at 20:23
  • Again, I am aware of this, but it is always good to note what the specs say, if for no other reason to understand exactly what you point out - that they are out of touch with reality and need to be updated. By the way, what is "js-hacks" and why is it needed? I noticed from your profile that you created it, but it's difficult to discover what it is since your website for it is lacking in information and I don't have time to read through all of the files looking for just that. – Jason Bunting May 04 '09 at 20:45
  • @Jason: js-hacks is a place to dump standalone scripts so that I can link to up-to-date versions; most of them are basically useless or just-for-fun, but there are also some which are pretty nice to have when you don't want to use a full-blown framework; problem is, I'm too lazy to write documentation, even if some scripts might deserve exposure to a more general public (e.g. http://mercurial.intuxication.org/hg/js-hacks/raw-file/tip/capture.js is pretty nice and changed my approach to event handling...) – Christoph May 04 '09 at 21:07
  • @Christoph: Ah, okay - I wasn't sure and thought maybe it was a framework of some sort. I am a fond user of MochiKit (been using it heavily for 3 years now) and have started using jQuery in the last 3 months, but I love JavaScript and enjoy learning new things about it, perhaps I will glean something useful from your scripts. Thanks. – Jason Bunting May 04 '09 at 21:17
3

Okay, I'll try to summarize the various partial answers here (including mine).

It would appear the answer is to use onload, which is "widely supported" though not officially required according to standards. Note that for IE in particular, you must set onLoad before setting the src attribute or it may not fire onload when loading an image from the cache. This seems like best practice in any case: set up your event handlers before you start firing events. Where onload is not supported, one should assume that the functionality simply isn't available.

Sebastian Good
  • 6,310
  • 2
  • 33
  • 57
1

HTML

<img id="myimage" src="http://farm4.static.flickr.com/3106/2819995451_23db3f25fe_t.jpg"/>​

SCRIPT

setTimeout(function(){
    $('#myimage').attr('src','http://cdn.redmondpie.com/wp-content/uploads/2011/05/canarylogo.png').load(function(){
        alert('Main Image Loaded');

        $('#myimage').attr('src','image.jpg').bind({
            load: function(){
                alert('Image loaded.');
            },
            error: function(){
                alert('Image not loaded.');
            }
        });
    });
},1000);

JS Fiddle

http://jsfiddle.net/gerst20051/sLge3/

Andrew
  • 3,733
  • 1
  • 35
  • 36
1

If im not mistaken in javascript an image tag has the events onload and onerror

here is an intersting bit of code i did to hide broken images

in a style tag

img.failed{
   display:none;
}

using the img onerror i had a piece of javascript that did this.className=failed and it would hide the image if it errored

however i may have misunderstood what your goal

Dominic Rodger
  • 97,747
  • 36
  • 197
  • 212
Jim
  • 601
  • 6
  • 17
  • 1
    Although a few browsers support it, the "onload" event is not supported by the W3C standard, and thus not all browsers need to or have to support this. – Jason Bunting May 04 '09 at 19:48
1

It would appear the answer is use onreadystatechange in IE, and onLoad in other browsers. If neither one is available, do something else. onLoad would be preferable, but IE doesn't fire it when loading an image from the cache, as noted in one of the answers below.

Sebastian Good
  • 6,310
  • 2
  • 33
  • 57
  • IE fires onload when loading from cache - you just have to make sure to set `onload` before setting `src`; the problem Jason mentiones is GWT-specific, which is mentioned on the linked page – Christoph May 06 '09 at 13:55
  • It would appear you're right -- thanks for diving even deeper to the end of the comment chain. Now I need to test it... – Sebastian Good May 07 '09 at 01:51
0

Using a data URI is the way to go. However, using an SVG instead of a GIF provides more flexibility. You can quickly change the size. Just run this in a JavaScript console:

var width = 1;
var height = 1;
'data:image/svg+xml;base64,' +
btoa(`<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}"/>`);

Example output:

data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxIiBoZWlnaHQ9IjEiLz4=

I threw together a quick CodePen utility to create transparent SVGs with arbitrary dimensions. It's pretty basic, so you'll need to modify it if you want to add color or other features to the image.

The biggest advantage over a single-pixel image is that it allows aspect ratio to be preserved. This is important for dynamically-sized images, as layout is often performed before the src changes to a real image (if that's the intended purpose). Until the src changes, image will be square, which may be undesirable.

Zenexer
  • 18,788
  • 9
  • 71
  • 77