39

I'm trying to perform an Ajax call with jQuery which is working well. I use the success event to display the data. It seems however that success gets triggered as soon as the external HTML file gets loaded. If there are large images they keep loading after they're shown. Is there a way to display the content after everything is fully loaded? Here is the code:

$('#divajax').html('<br><div style="text-align: center;"><img src="res/ajax-loader.gif"></div>');
$.ajax({
    cache: false,
    url: 'ajax/content.php',
    success: function(data) {
          $('#divajax').html(data);
    }
});
Teun Zengerink
  • 4,277
  • 5
  • 30
  • 32
robs
  • 649
  • 4
  • 13
  • 28

10 Answers10

49

The plugin that @alex wrote didn't work for me for some reason... I couldn't figure out why. But his code did inspire me to come up with a more lightweight solution that is working for me. It uses jquery promises. Please note that unlike @alex 's plugin, this doesn't attempt to account for background images on elements, only img elements.

// Fn to allow an event to fire after all images are loaded
$.fn.imagesLoaded = function () {

    // get all the images (excluding those with no src attribute)
    var $imgs = this.find('img[src!=""]');
    // if there's no images, just return an already resolved promise
    if (!$imgs.length) {return $.Deferred().resolve().promise();}

    // for each image, add a deferred object to the array which resolves when the image is loaded (or if loading fails)
    var dfds = [];  
    $imgs.each(function(){

        var dfd = $.Deferred();
        dfds.push(dfd);
        var img = new Image();
        img.onload = function(){dfd.resolve();}
        img.onerror = function(){dfd.resolve();}
        img.src = this.src;

    });

    // return a master promise object which will resolve when all the deferred objects have resolved
    // IE - when all the images are loaded
    return $.when.apply($,dfds);

}

Then you can use it something like this:

$.ajax({
    cache: false,
    url: 'ajax/content.php',
    success: function(data) {
        $('#divajax').html(data).imagesLoaded().then(function(){
            // do stuff after images are loaded here
        });
    }
});

Hopefully that's helpful for someone.

Note that using the above code, if one of the images errors (eg because the URL was wrong), the promise is resolved anyway and the error is ignored. This might be what you want, but, depending on your situation, you might instead want to abort whatever you are doing if an image fails to load. In which case you could replace the onerror line as follows:

img.onerror = function(){dfd.reject();}

And catch the error like this:

$('#divajax').html(data).imagesLoaded().done(function(){
    // do stuff after all images are loaded successfully here
}).fail(function(){
    // do stuff if any one of the images fails to load
});
Daniel Howard
  • 4,330
  • 3
  • 30
  • 23
  • this looks like it may create a memory leak. every time you make an ajax call, you create a duplicate of every image on the page in memory and attach an event to each new image object, keeping the image object in memory after the imagesLoaded function is done executing. Once all the duplicate image objects are loaded, when are they removed from memory? Directly after executing the onload event callback? – Mark Entingh Jul 14 '15 at 18:41
  • 3
    @MarkEntingh - you might be right about the memory leak - I'm not certain. Once the promise is resolved, are there any references left to the Image object in the closure? I don't think there are, so JS should garbage collect it. If you're worried about this, you could use an array of Image objects defined outside the function, and delete them explicitly in your 'then' function. Note - you said 'a duplicate of every image on the page' - that's only the case if you apply it to the whole document - $(document).imagesLoaded(). Often it will be a small subset, or just a single image. – Daniel Howard Jul 15 '15 at 07:57
  • 1
    I recommend also handling the onerror event, otherwise the callback may never be called. – Joshua Walsh Aug 28 '15 at 04:53
  • 2
    Good point @YM_Industries - I've updated the code to incorporate your suggestion. – Daniel Howard Sep 08 '15 at 07:15
  • What about CSS background images? – Krii Nov 05 '15 at 00:44
  • 1
    @Kril if you need to include background images too, you'll have to find a way to search for all of the background images in use by elements of 'this' and add them to $imgs array at the start. – Daniel Howard Nov 05 '15 at 08:17
18

You could use my jQuery plugin, waitForImages...

$.ajax({
    cache: false,
    url: 'ajax/content.php',
    success: function(data) {

         $('#divajax').html(data).hide().waitForImages(function() {
             $(this).show();
         });

    }
});

This will load the stuff into your element, hide it, and then reshow it once descendent img elements have loaded.

alex
  • 479,566
  • 201
  • 878
  • 984
  • 1
    @alex , can i use this for append also , basically i am doing infinite scroll and appending elements to the existing container along with other images – kobe Mar 31 '13 at 17:30
  • @alex , thanks very much for your plugin , i gave my container div in append and its working just fine – kobe Mar 31 '13 at 18:16
10

You can bind something to the load events to know when they're done:

$('<img>').bind('load', function() {
    $(this).appendTo('body');
});

Or you could use this plugin.

Teun Zengerink
  • 4,277
  • 5
  • 30
  • 32
Lyle Pratt
  • 5,636
  • 4
  • 27
  • 28
5

this triggers on every img load separately:

$('img').on('load', function() {
      // do something
});

i used:

var loaded = 0;
$('img').on('load', function() {
   loaded++;
   if(loaded == $('img').length){
      // do something
   }
});
Aureliano Far Suau
  • 6,531
  • 2
  • 22
  • 25
2

You can use imagesloaded plugin that is work with JavaScript and jQuery, try to use inside ajax success callback function on loaded content, and hide all content while imagesloaded callback function not trigger, see below sample code

$.ajax({
    cache: false,
    url: 'ajax/content.php',
    success: function(data) {
        var $divajax = $('#divajax').html(data).hide();
        $divajax.imagesLoaded(function() {
             $divajax.show();
         });

    }
});
Girish
  • 11,907
  • 3
  • 34
  • 51
0

Try this code. It works fine.

$.ajax({
    url: "../api/listings",
    type: 'GET',
    success: function (html) {
        $('#container').append(html);
        $('#container img').on('load', function(){console.log('images loaded')});
    };
});
0

I've got a small bug when no images are found by the plugin from Daniel's solution.

Just in case some else get the same issue:

Change:

// if there's no images, just return an already resolved promise
if (!$imgs.length) {return $.Deferred.resolve().promise();}

To :

// if there's no images, just return an already resolved promise
    if (!$imgs.length) {
        var dfd = $.Deferred();
        return dfd.resolve().promise();
    }
0

Look into using jquery's .load()

Which has a description of :

The load event is sent to an element when it and all sub-elements have been completely loaded. This event can be sent to any element associated with a URL: images, scripts, frames, iframes, and the window object.

AlanFoster
  • 8,156
  • 5
  • 35
  • 52
  • the thing is i cant turn off async calls with load and set the cache options right? – robs Jan 23 '11 at 16:27
0
$('#divajax').html('<br><div style="text-align: center;"><img src="res/ajax-loader.gif"></div>').load(function() {
$.ajax({
    cache: false,
    url: 'ajax/content.php',
    success: function(data) {
          $('#divajax').html(data);
    }
});
});
sv_in
  • 13,929
  • 9
  • 34
  • 55
-6

i think the best is using this

$(window).ready(function(){ //load your ajax})
lilsizzo
  • 366
  • 2
  • 18
  • 6
    This won't work, since `$(document).ready()` and `$(window).load()` don't fire after AJAX request. – Andreyco Mar 28 '12 at 10:12