dclowd9901
dclowd9901

Reputation: 6826

jQuery: Dynamic image handling (waiting for load)

I'm trying to write a plugin that has a built in function to wait until all images that are on the page to be loaded before it executes itself. $(window).load() only works if it's the initial load of the page, but if someone wants to pull down some HTML through AJAX that contains images, it doesn't work.

Is there any good way of doing this AND implementing it so that it can be self-contained in the plug-in? I've tried looping over $('img').complete, and I know that doesn't work (the images never load, and the browser crashes under a "script takes too long to complete" bug). For an example of what I'm trying to do, visit the page I'm looking to house the plugin at:

http://www.simplesli.de

If you go to the "more uses" section (click it on the nav bar), you'll see that the image slideshow won't load properly. I know the current code doesn't work, but it's just holding place until I figure something out.

Edit: Trying this solution, but it just doesn't work:

if($('.simpleSlide-slide img').size() > 0) {
    var loaded_materials = $('.simpleSlide-slide img').get();
} else {
    var loaded_materials = $('.simpleSlide-slide').get();
}

$(loaded_materials).live('load', function() {
         /* Image processing and loading code */
    });

Upvotes: 4

Views: 15022

Answers (4)

naugtur
naugtur

Reputation: 16915

I think You need to bind a function to global ajaxSuccess and then bind a load event to every image on page.

$.ajaxSuccess(function(){
 $('img').load(function(){
  //Your code here
 });
});

or when You expect lots of images:

$.ajaxSuccess(function(){
 $('img:not(.loaded)').addClass('.loaded').bind('load',function(){
  //Your code here
 });
});

If any of the above code doesn't work properly Tou might want to put the code in a callback that adds html to the site after adding it. using setTimeout in the above function might help too.

[edit]

You can generate images <img src="something" onload="$.imgHello()" /> in server-side code and increment some counter in each $.imgHello() function call if You know how many images are there going to be loaded. But this is not a solution that I'd recommend to anyone.

Upvotes: 3

noah
noah

Reputation: 21519

The problem with looping over the images and calling $('img').complete is that the browser UI is single threaded, so you're never giving it chance to load the images because it's always in your loop. You can use setTimeout to allow the browser to work. e.g.,

$.get("whatever",function(html) {
   $('#foo').html(html);
   var images = getTheImages();
   function checkImages() {
      var done = true;
      $.each(images,function() {
         done = done && this.complete;
         return done;
      });
      if(done) {
        fireEvent();
      } else {
        setTimeout(checkImages,100); // wait at least 100 ms and check again
      }
   }
});

Upvotes: 3

dclowd9901
dclowd9901

Reputation: 6826

After trying to accomplish this task in just about every conceivable fashion, this one was the one that finally worked, and a big thanks to Naugtur for pointing me in the right direction:

var no_of_images = $('img').size();

$.extend(no_of_images);

if(no_of_images > 0) {

    var images = new Array();
    var i = 0;
    $('img').each( function() {
        images[i] = $(this).attr('src');
        i++;
    });

    i = 0;

    $(images).each( function(){
        var imageObj = new Image();
        imageObj.src = images[i];
        $(imageObj).load( function() {
            no_of_images--;
            i++;
            if(no_of_images == 0){
                functionToPerform();
            }
        });
    });
} else {
    functionToPerform();
}

For a quick explanation of what's happening, I'll use an analogy. Imagine each image is a guest at your party. The lights are out. You flip on the lights, then race to each guest and give them a piece of paper, a pen and a cell phone, all the while tallying how many people you handed materials out to. They all start drawing furiously and then as soon as they finish their drawings, they call you and tell you. For each one that calls, you cross a tally off your board. When the last person calls you (when you run out of tally marks on your board), you tell them to hang up and call the pizza guy. It's pizza time. If there were no guests to begin with, you call the pizza guy yourself.

If you're thinking "Why did he create an Image() object for each $.load(), well, I tried going the traditional jQuery route of using a straight jQuery-style selector instead of the image Object. It was seemingly completely ignored (which crashed the page). I tried using Image() in conjunction with .onLoad, and that worked insofar as not crashing the page, but it didn't perform the function for which I wrote it.

This bit of code is self-contained, so I imagine it can be copypasta'd into any image-loading app. It seems small enough to me so as not to be intrusive. functionToPerform() basically acts as any function you would like to wait to perform until all images are loaded (in my case, it was the entire plugin). Toodles.

Upvotes: 2

Andy E
Andy E

Reputation: 344517

Have you thought of using live() to bind to all current and future img elements?

$('img').live('load', function ()
{
    alert("image loaded: "+this.src);
});

After your update, I can see the problem you're having. live() works best on a selector, whereas your using it on a jQuery-wrapped array of elements. Using live() like this is no different to using bind(). You should try something like this instead:

if($('.simpleSlide-slide img').size() > 0) {
    var loaded_materials = '.simpleSlide-slide img';
} else {
    var loaded_materials = '.simpleSlide-slide';
}

$(loaded_materials).live('load', function() {
         /* Image processing and loading code */
});

Upvotes: 1

Related Questions