Eduardo Ponce de Leon
Eduardo Ponce de Leon

Reputation: 9706

Take an action only after AJAX has loaded all bytes of new content

I am trying to bring from the server some content using AJAX, I don't know how long it might take to load as some times it might be heavy. So I created a loading gif that appears everytime I call the AJAX function, but I want to fade it out once the AJAX response is loaded and fade in the content division.

HTML

<div id="loading"><img src="loading.gif" /></div>
<div id="content"></div>

This is what I am trying:

$(elm).click(function(){
  $('#loading').show();
  $('#content').fadeOut();
  $.ajax({
      url:'posts.php'
      data:{id:id_post},
      method:"POST",
      success:function(response){
        $('#content').load(response, function(){
          $('#content').html(response);
          $('#content').fadeIn();
          $('#loading').hide();
        });
      }
  });
});

I know it is wrong because in .load(param1, function(data){}), param1 should be a reference file to load and not a variable with a string, it seems to work, but console.log(data) returns me a not found error, so I am sure this is not the way to do it.

In short words: I want to fadeIn() my div and fadeOut() my loading if and only if every byte of the AJAX response is loaded, not if the code is ready, but loaded, for example a big image inside an <img> tag.

Any ideas? Thanks

Upvotes: 0

Views: 1025

Answers (4)

gitaarik
gitaarik

Reputation: 46350

If your AJAX response is a chunk of HTML that includes an <img> tag that you put on the page, and you want to detect when this image has been loaded, you should do something extra.

You should divide the response in the image to preload, and the raw html that uses the image. You could use a JSON response like this:

{
 "image": "url_to_big_image.jpg", 
 "html": "<img src=\"url_to_big_image.jpg\">"
}

Your favorite backend language probably has a way to convert your objects/arrays to JSON.

Then when your AJAX request has finished, you can do this:

var img = $('<img/>')
img[0].src = response.image // this pre-loads the image

img.load(function() {
    // now the image has been pre-loaded
    $('#content').html(response.html)
    $('#content').fadeIn()
    $('#loading').hide()
})

Multiple images

To do this for multiple images you could put the images in an array in the JSON:

{
 "images": ["url_to_big_image.jpg", "second_image.jpg"], 
 "html": "<img src=\"url_to_big_image.jpg\"> <img src=\"second_image.jpg\">"
}

Then loop through them in javascript, and keep up how many have been loaded. If all images have been loaded you can show the content:

var show_content = function() {

    // only show content if all images are loaded
    if(images_loaded == response.images.length) {
        $('#content').html(response.html)
        $('#content').fadeIn()
        $('#loading').hide()
    }

}

var images_loaded = 0

for(var img_url in response.images) {

    var img = $('<img/>')
    img[0].src = img_url // preload image

    img.load(function() { // when image is pre-loaded
        images_loaded++ // increase images_loaded
        show_content() // show content if all images are loaded
    })

}

I haven't tested this code so it might be you need to change something to make it work, but it's about the idea.

Upvotes: 2

A. Wolff
A. Wolff

Reputation: 74420

You could try this { not tested }:

success: function (response) {
     var $tmp = $($.trim(response)),
         $l_elems = $tmp.find('img, script'),  //find will not work if elements not nested inside a container. In this case use a fake div as container and nesting elements inside it
         tot = $l_elems.length,
         i = 0;
     $l_elems.one('load', function () { //here using one, not on
         i++;
         if (i === tot) {
             $('#loading').hide();
             $('#content').append($l_elems).fadeIn();
         }
     }).each(function () {
         if (this.complete) $(this).load();
     });
 }

Upvotes: 0

gitaarik
gitaarik

Reputation: 46350

The jQuery load function loads data from a server and takes an url as first argument. See the documentation.

So I think you should just do this:

success:function(response){
    $('#content').html(response)
    $('#content').fadeIn();
    $('#loading').hide();
}

Upvotes: 0

TecHunter
TecHunter

Reputation: 6141

Based on API you could do

$(elm).click(function(){
  $('#loading').show();
  $('#content').fadeOut();
  $('#content').load('posts.php',
          {id:id_post},
          function(response, status, xhr){
              $('#content').fadeIn();
              $('#loading').hide();
          }
   );
});

$.load already set the html of #content with the response.

Upvotes: 1

Related Questions