Are you attaching an onload function to an image but finding it doesn't always get called?
The problem is almost always this: you need to set the onload before you set the src. The reason is obvious when you think about it: if the image is in the cache, the image will be loaded immediately after setting the src, even before the onload is set.
Adding fuel to the fire, Firefox and Safari will let you set an onload immediately after (it seems they don't call the onload until the block of JavaScript finishes). This doesn't happen in Internet Explorer or Opera.
Long story short:
// evil:
var image = new Image();
image.src = 'image.jpg';
image.onload = function() {
// sometimes called
alert('image loaded');
};
// good:
var image = new Image();
image.onload = function() {
// always called
alert('image loaded');
};
image.src = 'image.jpg';
No matter how many times I come across this bug and find the solution, I end up making the same mistake a few weeks later. I'm hoping that blogging about it will get this stuck in my long-term memory.
curious as to the need for attaching an onload function to an image.
no, i'm not being a s/a, i'm a noob and want to know. i thoroughly understand you css, your code, and the explanation, now looking for the application/why.
Thanks! And thanks for your blog as well.
@Michael - Sometimes I don't want to show an image until it's completely loaded - for example, you might want to show a low-quality image in the meantime, and swap in the high quality image only when it finishes downloading.
I'm sure there are other uses but that's what I mostly use it for. :)
that totally makes sense.
you're enhancing the user's experience by not
causing the dreaded
'wait for the holes to fill in' syndrome.
thanks for this insight to the technique.
this would give an opportunity for sites to display better quality jpgs but do so without forcing the user to wait. substituting as you mentioned, a lesser quality jpg/image in its place whilst the primo one loads
curiosity again, is this technique a replacement for the lowsrc="xxx.jpg" option?
again though- Thanks for your quick reply.
@Michael - well I'm using it in photo galleries, where the thumbnail image is already loaded.. so I just stretch the thumbnail and put it in place.
I hadn't considered using lowsrc, perhaps that would work just as well. I'll have to try it out. Thanks for the tip.
another use is to display images in a slide show or a similiar application that would necessitate showing an image, and cache-ing the next one to come. so, when image1 loads, start loading image2 and so on. this is particularly valuable when you consider that IE has an issue downloading more than one file from the same host name. in this case you could cascade you image downloading as it may be faster/more fitting to download one image after another.
Just wanted to say thanks for the tip -- I was running into this problem myself and was getting pretty frustrated that it was only causing issues in IE7 (in my particular case), and seemingly intermittently (because of the caching). Moving the Image.src after the Image.onload solved it!
This is neither a bug or a problem. The logic behind it is like this:
The command "image.src = url" fires the downloading method of the image placed in the url you provided. Once that image is finished loading, it fires its onload event. If you define no method in the onload event by the time it is fired, then nothing is in fact supposed to happen on this event.
To me it totally makes sense to first <em>define</em> the method to be called once the onload event is fired, and only then to <em>fire it</em>.
Thanks, Jessie. Very useful.
Thanks Jesse, this saved me when the panic started setting in while working on a project late one night.
this Image() class is quite strange...example:
function(url){
var img = new Image();
img.onload = function() {};
img.src = url;
var width = img.width;
imgArr.push(width);
var height = img.height;
imgArr.push(height);
return imgArr;
}
ie6 - works fine, but in ie7 nope.. it can't upload a picture and define pictures parameters.. also ie7 popups an error becouse unLoad should be written with big 'L'.. so till now i don't know ka to solve the problem..
good luck!
Thanks!
Was getting frustrated why my onload didnt trigger 100% (ie7),
Really saved me some useless wasting of time ;).
While the aesthetic explanations are valid, and I've even used it for that purpose myself, another reason for Image.onload is that the width and height of an image cannot be known until the browser has downloaded the header of the image file. If you access the width and height properties of the image object too soon, you get garbage. If you wait for the onload function to fire, you know those values are there. That may explain why Darius is getting inconsistent results. You need to be accessing the width and height from within the onload function. Unfortunately, this would require some restructuring of your application, since the outer function would not be able to return the values.
thanks that tip helped me alot today on IE7
Simply, you saved my life. Period.
If you come in Rome i need to offer a beer ;)
After an hour of changing things I went searching and came across this post.
Sometimes you feel really stupid!
I am hoping that commenting here will put this in my long term memory!
Thank you
Great post.
Thanks for this...
Unfortunately, in XHTML (and HTML 4.01) "onload", "onerror", and "onabort" are all invalid attributes of the IMG tag. I wrote JavaScript code for my site's home page so that the page displays only when all images are displayed (or error out, or the load is aborted by the user) by assigning a function to each of those three properties to every IMG tag on the page. But now my page fails W3C XHTML validation...!
So seeing your JS code above made me think "Hey, maybe the onload attribute is valid in the DOM, but not in HTML", but going a script-only route would mean anyone without JS enabled would be screwed. And... checking the DOM Level 2 spec, I see in section 1.6.5:
"The load event occurs when the DOM implementation finishes loading all content within a document, all frames within a FRAMESET, or an OBJECT element."
No mention of the IMG element.
Urgh! Should we anticipate the disappearance of these event handling attributes from IMG since they're nonstandard? Or have I misread the standard (as I've done in the past)?
@Alex - well, whether you use HTML attributes or use DOM scripting, it's a JS-only solution either way.
I suggest you use DOM scripting because it works, and that way your HTML will remain valid.
Thanks for posting this! It seems so obvious once you explain it, but it can have you scratching your head for ages until Enlightenment dawns. Thank you!
This was really helpful!! thanks a ton Jesse! spent all day...and finally stumbled across this article -- wish i'd seen it sooner:)
Strange, but the .onLoad doesn't works as supposed to over here.
It fires before the images has finished loading:
preload_image = new Image();
preload_image.onLoad= startTimer();
preload_image.src=image;
Online version:
http://jan-kask.dreamhosters.com/snazzy/test/
( the full-view images should load first.. and then fade in. )
any ideas ?
Mozilla Firefox 2.0.0.13
All hail!
I've been looking into this same issue until I found this post, I'll try now to remeber it myself.
thanks!
r
Great! you save me!! thanks to you and google :-)
Hi,
Thanks for posting this, I was looking in the wrong direction, thinking IE7 was the problem, when it was actually oversight on my part.. Your post really helped out!!
Seriously, thank you for sharing this!
Very helpful tip!
After making the suggested change, I was still getting problems with IE not firing the onload when trying to load multiple images.
I believe that the problem is that, as another commenter noted, IE limits the number of connections it can make to the same server (probably for the sake of politeness).
To get around this limitation for multiple images, I ended up loading the next image after the previous onload function was done. Something like:
var images = ['image1.jpg', 'image2.jpg', 'image3.jpg'];
var index = 0;
function loadNextImage() {
if (index < images.length) {
var image = new Image();
image.onload = onImageLoad;
image.src =images[index++];
}
}
function onImageLoad() {
// Do stuff with image
// ....
// Do a setTimeout to call loadNextImage instead of calling
// it directly, which might make a recursive call and cause
// a stack overflow with lots of images.
setTimeout('loadNextImage()', 10);
}
loadNextImage();
Thanks for the tip!
I think a lot of people instinctively set the src and then the onload because that is the order they expect the code to execute. But then you wouldn't call a function before you declare it, would you?
This is one of the catches of defining functions at runtime. I think a general rule to follow in all languages is that if you must define a function at runtime, do it at the earliest possible point in your code.
I may say it is a bug because HTML and JS are non procedural.
This is how it works:
1. The browser loads the code.
2. Builds the DOM.
3. Launches events and functions.
Gecko and KHTML are doing their job right, Trident and Presto probably not (As usual)...
You are awesome! Fixed my problem on IE7! Thanks!
it worked!!! Oh my God, I love you so much. I was about to kill myself... you saved my life! Thank you!
Switching the "onload" and "scr" lines solved half my problem. (Thanks!) I have the same problem as Jan Van Lysebettens. Both IE and firfox trigger the onLoad function, but starts downloading the images (again?) when I try to use them later. Any suggestions?
Thanks alot for the article! Saved me lots of potential time loss and frustration :)
After coming to the article for a second time, I figured I'd offer a overdue thank you.
Cheers.
How I can get current values such as images height/width using this method?
@LFFATE - you can access them in the onload function:
var image = new Image();
image.onload = function(){
var width = this.width;
var height = this.height;
// do something with width and height
};
image.src = 'http://somewhere.on/the/internet.gif';
thankyou thankyou thankyou thankyou.
makes sense but I had totally overlooked it.
sometime better use document.createElement( 'img' ), because new Image() don't store in browser cache.
Wow! I almost spend hours to debug this and your solution is oth simple as it is effective!
Thankx for sharing!
thank you very much! IE7 was causing me grief with this...
Hello Jesse,
The below didn't work in firefox 3. Somehow the onload event didn't fire. Can u suggest any other approach.
// good: var image = new Image(); image.onload = function() { // always called alert('image loaded'); }; image.src = 'image.jpg';
Thanks...
You save me.
Man, you are the man.
I FOUND the ANSWER!! :D
First, tried as suggested in the blog, and nope, it did not solve the problem -- sometimes it fires, sometimes it won't.
Preloader.prototype.LoadImg = function(url) {
var newImage = new Image();
newImage.onload = this.Loaded.bind(this);
newImage.src = url;
};
But then, OUT OF BLUE, I had the impossible guess in my head: could it be the variable SCOPE problem?
In this case, the image object has no external reference, so it *might* be possible that, when the image was loaded too fast, the *finished* image object got caught by the garbage collector before the javascript call stack is cleaned, and it never had the chance to place the onload call.
So I added an array outside of the function to hold the image object:
Preloader.prototype.LoadImg = function(url) {
var newImage = new Image();
newImage.onload = this.Loaded.bind(this);
newImage.src = url;
this.Images.push(newImage); // Keep the reference!
};
Guess what? Now IE RELIABLY fires onload event, regardless whether the image is in the cache or not!
LOL, I guess it is just another case IE puts a bullet in its own feet...
Okay, life just never gets easier... :D
I just (re)discovered another quirk in IE, which makes using onload event not so desirable.
The problem is that when loading animated GIF, the image object in IE fires onload each time the animation sequence repeats. (Funny that in this case, there is no scope problem anymore...)
To make the problem worse, removing all event handlers from the animated GIF image the first time onload event fires triggers another quirk in IE. Doing so, then create another animated GIF image with *same* url immediately after that, the new image shows up as image placeholder for a very short time window.
(If I don't touch the preloaded GIF image object, the new instance of image is rendered immediately, smooth and seamless, but then I have to endure the endless onload event...)
Finally I solved the problem by hooking onreadystatechange event and watch for the readyState to become 'complete', which, fortunately, is a one-time process for both static and animated image. But, since it is an IE only event for image object, the script has check the browser and hook different events (onload or onreadystatechange) accordingly.
I don't know what version of IE you are using, Adam. My version of IE does not animate the GIF at all. The onload event fires reliably. Reporting Services has to render a chart, which takes a few seconds. I am displaying the animated GIF in its place. Firefox animates it nicely while waiting for the chart image. IE does not, which defeats the purpose of having it there, sort of. I am looking at ways around this bug.
I did not run into issues with the order of the SRC and ONLOAD parameters.
The solution was to combine two techniques - CSS preloading and JS to swap the image. Because IE suspends the GIF animation while another image for the same tag is loading in the background, a 2nd (hidden) image tag was needed.
See http://cdonner.com/the-hrml-img-tab-and-onload-event-bugs-in-ie.htm for the code.
validating with http://validator.w3.org/check it says onload is not allowed inside img tag, (using doc type transitional)
??
Inline Javascript will invalidate your pages. For those of you wondering for a way around this, simply apply the DOM Level 2 model for catching events. It is the modern, preferred approach, and much more efficient than the traditional inline registration model (DOM 0).
Here's an example:
var image = new Image();
image.addEventListener("load", function(){}, false);
image.src = 'image.jpg';
You can either apply the anonymous function to the event listener as done above, or you can define a function elsewhere and write the name of the function where it says function() above, minus the parentheses.
Internet Explorer has it's own event handler (attachEvent), so I suggest reading up on the browser differences, and finding some generic addEvent function online somewhere. There are loads of them.
By the way, thanks for the insight, Jesse. :D
Had a problem with this. Thanks for the solution!
Dude you just saved me hours of time...after a good hour of aggravations trying to figure out why my widget wasn't working on IE....and only sometimes on Firefox. Thanks! Whew!
WOW your tip for postponing the .src assignment on images REALLY helped
this is quite interesting - i guess the problem is due to primed cache. just wanted to say, thank you.
Hi All,
I still have issues with src and onload working together in sync.
my code looks like this.
<c:when test="${sessionScope.pictureFile != null}" >
<img style="visibility=hidden" src='<c:out value="${sessionScope.pictureFile}" />' id="bankerPic" alt="" onload="window.setTimeout(resizePicture, 30)"/>
</c:when>
if (imageElement != null) {
// to make sure Height && width not equal to zero
h = imageElement.height;
w = imageElement.width;
if ((w > 150) || (h > 210)) {
// Picture bigger than min size - needs to be resized
var dimension = h/w;
if (dimension > 1) {
dimension = w/h;
newW = hSize * dimension;
imageElement.width = newW;
imageElement.height = hSize;
} else {
newH = wSize * dimension; imageElement.height = newH; imageElement.width = wSize;
}
}
}
imageElement.style.visibility='visible';
}
Can some body help me plsssssssss.
So simple, but wouldn't have guessed it - thanks!
THANK YOU! I would never have guessed about putting the src after the function. makes a lot of sense, but just didn't think about it!
D'oh... I just wasted an hour on such an obvious thing... Thanks!
classic micro$oft buggy crap!
i'm glad you posted about it, i was ready to make and sink into some creepy workaround.. thanks
I love those quick-n-easy solutions. Big thank you.
major props. I thought this was a cache issue and was about to write some hacktastic code. Thank you!
Nicely written up, many thanks! (Was just about to start puzzling over IE's image-caching mechanics..)
OMFG thank you very much!
CSS and input button with hover states need an onload() event handler; otherwise, you'll get a "flicker". The background image called in the CSS file is not displayed until its fully loaded.
Hi Jesse,
I was struggling with this problem for few days :)
thanks to YOU I can sleep peacefully again :D
Thank you so much. I have been working on this problem for 1.5days now solid. I was about to rip my hair out.
Cheers, just what I was looking for, it solved the problem with IE8 but Opera 10.10 doesn't seem to like it?
var inspection = new Image();
inspection.onload = function() {
alert('Found image after load');
}
inspection.src = imagePath;
Sorry didn't get a chance to test it on the previous version to 10.10 so that needs confirming.
Cheers
Dal
I'm very interested by this thread, but I can see where is the solution :/
I'm trying to find a way to calculate the time to download an image : timestamp before, then another timestamp triggered by the onload, and calculate the diff
var ndc_time_start = (new Date()).getTime();
NDC = new Image();
NDC.onLoad = NDC_();
function NDC_()
{
var ndc_time_stop = (new Date()).getTime();
var ndc_time_diff = ((ndc_time_stop - ndc_time_start) / 1000);
document.write("Start Time for NDC "+ndc_time_start+" ms<br>")
document.write("Stop Time for NDC "+ndc_time_stop+" ms<br>")
document.write("Diff for NDC "+ndc_time_diff+" ms<br>")
}
NDC.src = 'http://img25.imageshack.us/img25/5315/n382wawoamd11fehbk31220.jpg';
As said, the src is after the onload, but it seems the onload function is called before the img has loaded.
Same with IE7 or firefox 3.5.6
any idea ?
Hi Jack,
I'd hazard a guess at the problem being with line;
NDC = new Image();
(by the way, if it's not a constant I would advise against the use of capitals, pick a camelCase or words seperated with '_', sorry, pet hate :/ )
Anyway, try;
var ndc = new Image();
I tend to always declare a variable first as I fell into problems with this in javascript;
for(n = 0; n < x; n++)
Seems memory leaks are the reason why things dont go to plan and they are often the hardest to figure out!
I subsequently sorted this issue by doing;
for(var n = 0; n < x; n++
Cheers
James
Please post back if it worked/failed so others can may be helped.
@Jack Actually I'm thinking it may be this line:
NDC.onLoad = NDC_();
because that assigns the onload to the result of the NDC_ function, and that function doesn't return anything. And that is also why the function is being called before the image loads, it's being called on that line.
What you want to do instead is assign to the function itself by leaving off the ():
NDC.onload = NDC_;
And James/Dal is right, using 'var' is probably a good idea too.
updates :)
Having var ndc = new Image(); works as before, not better, not worse, but i'll keep it like that as it's clearner.
Behaviour : It prints everything, then load the image in background.
Leaving off the () is a great idea too, but it seems it breaks the script. The function is not called, I got nothing printed :/
Thanks for your help guys.
I may add that the script is in <body>, with nothing in <head>
dunno if it's important ...
<body onload="NDC_()"> works fine, but then I calculate the time to load the whole page, not only one pic.
Something wrong with the image onload event
@Jack - I think the problem is using 'document.write' after the page is done loading. This only works while the page loads and not in event callbacks.
Try using some DOM scripting methods instead.
Replace:
NDC.onLoad = NDC_;
with:
NDC.onload = NDC_;
The "l" in "onload" should not be capitalized. JavaScript is case sensitive.
Removing the capital L is not helping :/
Jesse, that could be an explanation yes. But it means there is not simple solution :/
Here's the code for the version that I put together to test. I even added an alert box to account for situations where document.write won't work. This is working for me under the following configurations:
Firefox 3.0.13 / Mac OS X 10.4.11
Firefox 3.0.15 / Microsoft Windows Server 2003 Standard Edition Service Pack 2
Internet Explorer 7.0.5730.11 / Microsoft Windows Server 2003 Standard Edition Service Pack 2
<html>
<head>
<title>onload test</title>
</head>
<body>
<script language="javascript">
var ndc_time_start = (new Date()).getTime();
var NDC = new Image();
NDC.onload = NDC_;
function NDC_()
{
var ndc_time_stop = (new Date()).getTime();
var ndc_time_diff = ((ndc_time_stop - ndc_time_start) / 1000);
alert("Start Time for NDC "+ndc_time_start+" ms\n"
+ "Stop Time for NDC "+ndc_time_stop+" ms\n"
+ "Diff for NDC "+ndc_time_diff+" ms");
document.write("Start Time for NDC "+ndc_time_start+" ms<br>")
document.write("Stop Time for NDC "+ndc_time_stop+" ms<br>")
document.write("Diff for NDC "+ndc_time_diff+" ms<br>")
}
NDC.src = 'http://img25.imageshack.us/img25/5315/n382wawoamd11fehbk31220.jpg';
</script>
</body>
</html>
Thank you for that.
It seems document.write has a strange behaviour indeed.
I also get some strange behaviour with both IE and firefox, the script does not end in a clean way. It looks like the browser hang and I can't start a refresh with control+F5. Do you have the same problem ?
Yes I do. As Jesse pointed out, using the write function after the page has finished loading is not proper. If you think about it, "write" inserts code into the HTML stream. If the browser has already received the "</html>" tag, you're adding code after that.
You can get around this using innerHTML. In the body of the document put:
<div id="ndc_div">
</div>
In place of the write's put:
var ndc_div = document.getElementById("ndc_div");
ndc_div.innerHTML = "Start Time for NDC "+ndc_time_start+" ms<br>"
+ "Stop Time for NDC "+ndc_time_stop+" ms<br>"
+ "Diff for NDC "+ndc_time_diff+" ms";
Thank you very much guys !
I have learned a lot a the script is now working fine.
great the Image onload not always working issue took me ages to solve ... and I couldn't .. thanks mate!
Your post helped me-thanks!
Dude couldn't figure it out. Thanks for helping me out.