Skip to content

Instantly share code, notes, and snippets.

@paulirish
Created January 4, 2010 02:38
Show Gist options
  • Save paulirish/268257 to your computer and use it in GitHub Desktop.
Save paulirish/268257 to your computer and use it in GitHub Desktop.
imagesLoaded() jquery plugin
// $('img.photo',this).imagesLoaded(myFunction)
// execute a callback when all images have loaded.
// needed because .load() doesn't work on cached images
// mit license. paul irish. 2010.
// webkit fix from Oren Solomianik. thx!
// callback function is passed the last image to load
// as an argument, and the collection as `this`
$.fn.imagesLoaded = function(callback){
var elems = this.filter('img'),
len = elems.length,
blank = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
elems.bind('load.imgloaded',function(){
if (--len <= 0 && this.src !== blank){
elems.unbind('load.imgloaded');
callback.call(elems,this);
}
}).each(function(){
// cached images don't fire load sometimes, so we reset src.
if (this.complete || this.complete === undefined){
var src = this.src;
// webkit hack from http://groups.google.com/group/jquery-dev/browse_thread/thread/eee6ab7b2da50e1f
// data uri bypasses webkit log warning (thx doug jones)
this.src = blank;
this.src = src;
}
});
return this;
};
@Akaryatrh
Copy link

Ok, i found a fix !
We just need to unbind the load event, before binding it :

line 17 : elems.unbind('load').bind('load',function(){ // what goes next });

(i updated my demo with this fix)

@paulwehner
Copy link

The imagesLoaded() plugin doesn't seem to be working for my in this example... http://jsfiddle.net/aaairc/fANSQ/1/ ... using my Mac's Chrome Browser. To test, click the "Click Me" div and see if boxHeight is printed out as 36 or 543. If it prints out as 36, that means (I believe) that when the height of the popup was measured, .imagesLoaded didn't wait for the image within the popup to load.

For my computer, boxHeight prints out correctly in FF, Safari, and IE. In Chrome 12.0.742.112 on my Mac, it prints out as 36. Strange behavior though... apparently boxHeight was measured correctly when I asked someone else on IRC who had a Mac to test. Not sure if his version of Chrome is the same.

I tried clearing the cache and cookies off my Chrome Browser, but still the same behavior. So after that longwinded explanation:

  1. Can anyone else see this bug, where boxHeight is printed as 36 in Chrome, instead of the actual height of the box of 543?

  2. If they can... how do you fix it?

@cjboco
Copy link

cjboco commented Jul 10, 2011

@aaairc, I'm not a jFiddle expert, but I don't see that you've included the imagesLoaded plug-in in the example?

@Akaryatrh
Copy link

The plug-in is included in his file called "jquery.isotope.js" (check "manage resources" on left menu) : http://isotope.metafizzy.co/jquery.isotope.min.js
I'm personnaly on a Mac, and testing your example on Chrome 12.0.742.112.

boxHeight returns me 543, so i can't reproduce your bug.

@paulwehner
Copy link

hmmm not sure why it would be something specific to just my chrome browser, but I'll try and reinstall my browser and see if it continues.

@Akaryatrh
Copy link

Oh just a note : why did you declare your functions (adjustHTML() and adjustPosition() ) out of the $('document').ready(), at the end of your script ? This is not valid (according to JSLint), you can wrap them inside to prevent any error or bug.

@paulwehner
Copy link

I did it because I don't know what I'm doing :). Just learning jQuery. Thanks for the heads up.

@scottneish
Copy link

Ok. To anyone that is having issues with IE not triggering consistently, a friend put me on to a solution that seems to work...

current code (line 17):
elems.bind('load',function(){

revised code -> i is an integer for iterating through all images in 'elems'; I had to wrap the code from line 17-28 in a for loop to iterate through.
$("⟨img src = " + elems[i].src + "/⟩").bind('load',function(){
(make sure to relpace the < > when you paste)

Hope this helps somebody!

@cjboco
Copy link

cjboco commented Jul 21, 2011

Wouldn't that suggest that "this.filter("img")" isn't working properly in IE? Since in theory, elems should already be an array of images.

@scottneish
Copy link

I don't believe it's the filter, but how IE handles the element passed into the .load function... I have had issues when passing something as simple as $("#myImage").

@ksykulev
Copy link

You should probably unbind the load event when the image is loaded (line 17).
...
elems.bind('load',function(){
$(this).unbind('load');
if (--len <= 0 && this.src !== blank){ callback.call(elems,this); }
}
...
I abuse this script by replacing the src for some images multiple times, which binds the load event multiple times. This in turn causes the callback to be called many times on subsequent calls to imagesLoaded, since the old load events have 'len' set to zero on completion.

@ksykulev
Copy link

Here's my revised version.. https://gist.github.com/1170048
Line 18 has the notable change.

@cjboco
Copy link

cjboco commented Aug 25, 2011

@ksykulev - Just a quick thought, would this cancel the load event on the real image, since this would unbind the load event when setting the blank image? Haven't tested it yet, just curious.

@ksykulev
Copy link

@cjboco - hmmm good point, i didn't think about that..
In my particular case I have a gallery of images, and i give the user the ability to switch between the small or large res. images. I need to rescale some of elements around the images, so I use this script to let me know when all the images are loaded.
It appears to work OK in webkit... but some more thorough testing is definitely needed.

Not sure if this helps, but I also namespaced the binded load event like this:
elems.bind('load.imagesLoaded',function(){
$(this).unbind('load.imagesLoaded');
if (--len <= 0 && this.src !== blank){ callback.call(elems,this); }
}

@ksykulev
Copy link

Yes that does break it in webkit, scratch that
Only way around that is to break the cache:
src + ?_="+(new Date().getTime())
:(
Thanks for pointing that out.

Could the unbinding happen inside the callback after all the images have loaded?
elems.bind('load.imagesLoaded',function(){
if (--len <= 0 && this.src !== blank){ elems.unbind('load.imagesLoaded'); callback.call(elems,this); }
}

@cjboco
Copy link

cjboco commented Aug 25, 2011

@ksykulev - maybe add it after "this.src = src"?

@ksykulev
Copy link

@cjboco - After some testing:
Putting the unbind after the "this.src = src" unbinds the load event too fast and it doesn't get called..
Putting the unbind in the conditional seems to be working OK.

elems.bind('load.imagesLoaded',function(){
if (--len <= 0 && this.src !== blank){ elems.unbind('load.imagesLoaded'); callback.call(elems,this); }
}

@cjboco - Thanks for your help!

@paulirish
Copy link
Author

hey guys

mr @desandro has picked up this script and given it a proper repo as a home:

https://github.com/desandro/imagesloaded

I've added the namespaced events to my gist above as that seems like a good idea. but in general, david's version is the new canonical one

@StephanFischer
Copy link

ok, its a cached images solution for webkit? but its simpler by checking width or height after loading event:

    img.addEventListener("load", function() 
    {
      var tmrLoaded = window.setInterval(function()
      {
        if (img.width) 
        {
          window.clearInterval(tmrLoaded);  
          alert( "its done!");
        }
      }, 100);

    }, false);

Greetz

@ahoward
Copy link

ahoward commented Feb 10, 2013

nice! i'm using this here -> https://github.com/ahoward/jquery.bires

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment