0

When the document is ready I load this list of images through an ajax call:

$.ajax({
        url:'mysite.it/ajax/list_filter.php',
        data:data,
        type:'POST'
    }).done(function(data){
        var response = $.parseJSON(data);
        var people = '';
        if(response.success){ 
            if(response.showcase){
                $.each((response.showcase),function(index,item){
                    people += '<li>';                
                    people += '<img src="'mysite.it/pict_thumbs/' +item.pool_user_pict+ '">';
                    people += '<label>' +item.username+ '</label>';                       
                    people += '</li>';
                });
            }
        }
});

Since the list can be huge, I want to call a callback function only after all the images are loaded and displayed and the list is ready.
How can I do this??

Ferex
  • 553
  • 6
  • 22
  • possible duplicate of [jQuery callback on image load (even when the image is cached)](http://stackoverflow.com/questions/3877027/jquery-callback-on-image-load-even-when-the-image-is-cached) – Bram Vanroy Nov 16 '14 at 11:41
  • The one you mention doesn't make use of Ajax calls – Ferex Nov 16 '14 at 14:44

2 Answers2

1
function loadImages(images, callback) {
    var length, count, check, i, img;

    length = images.length;
    count = length;

    check = function () {
        if (--count === 0) {
            callback();
        }
    };

    for (i = 0; i < length; i++) {
        img = new Image();

        img.onload = check;
        img.onerror = check;

        img.src = images[i]
    }
}

var images = [], html = '';

$.each(response.showcase, function(index, item) {
    html += '<li>';                
    html += '<img src="mysite.it/pict_thumbs/' + item.pool_user_pict + '">';
    html += '<label>' + item.username + '</label>';                       
    html += '</li>';

    images.push(item.pool_user_pict);
});

loadImages(images, function () {
    $('#some-element').html(html);
});

JSFiddle: http://jsfiddle.net/v2dyfdyu/

knpsck
  • 833
  • 8
  • 8
  • Wonderful, to say the least... I thought I had to something by counting image elements but I didn't think about image preloads. Do .onload and .onerror are consistent across browsers? – Ferex Nov 16 '14 at 15:34
  • Yes, should work even in IE8. This answer may be helpful for you: http://stackoverflow.com/questions/10403983/cross-browser-image-onload-event-handling – knpsck Nov 16 '14 at 17:09
0

I think that async library would suit perfect here.

$.ajax({
        url:'mysite.it/ajax/list_filter.php',
        data:data,
        type:'POST'
    }).done(function(data){
        var response = $.parseJSON(data);
        var people = $('<ul />');
        if(response.success && response.showcase) {
            async.map(response.showcase, function(item, done) {
                var img = new Image();

                //we now install callbacks to certain events
                img.addEventListener('load', function () {
                    done(null, {
                        img: img,
                        username: item.username
                    });
                });

                img.addEventListener('error', function () {
                    done(true);
                });

                //this will kick image loading
                img.src = 'http://mysite.it/pict_thumbs/' + item.pool_user_pict;
            }, function (err, results) {
                if(err) {
                    //images weren't loaded correctly, handle this whatever you like
                    console.error('Something went wrong');
                    return;
                }

                //here you now know that every image you wanted is loaded
                results.forEach(function (data) {
                    var li = $('<li />');
                    li.append(data.img);
                    li.append($('<label />', {
                        html: data.username
                    }));

                    people.append(li);
                });
            });
        }
    });

Method async.map which I used works in the way that it invokes it's second argument (the iterator) on each element of the array and then wait until all the done functions were called. done function is a function that gets an error as its first argument (null if there was no error) and a result of an asynchronous operation - in our case, a loaded image element, which is pushed to a new array.

When all the dones have been called, async then calls a third argument (the callback), passing it an error (or null if there was no error) and an array, created from all the asynchronous callbacks. Then we can just iterate over them and append the images and usernames to the html element.

Here is fiddle that demonstrates it (using fake data, saved in the response variable): http://jsfiddle.net/wxx6veqb/

Kuba Jagoda
  • 4,908
  • 2
  • 19
  • 21
  • I hate using libraries, if I have to accomplish just one task – Ferex Nov 16 '14 at 15:34
  • Well, you could have also said this about using `$.ajax` instead of `XMLHttpRequest` and `$.parseJSON` instead of `JSON.parse`, especially that later you build HTML by string concatenation which actually begs for using jQuery. Async is a lib especially designed for this type of task and there's nothing wrong with using a tool that someone actually wrote, instead of writing it ourself. – Kuba Jagoda Nov 16 '14 at 16:30