4

In a jQuery project, I am adding several images to a page after it has finished loading, using the append() function. After all the appends are done, I need to call a function that detects the width and height of each of these new elements, so I can properly align them in the page.

This is working well on my rather slow development PC. But when testing it on a fast machine in a quick browser (e.g. Chrome), it turns out that calling the layout function right after the appends are done leads to random, weird behavior. Executing the layout function again with a slight delay solves the problem. I guess "javascript is done appending the new elements" is not the same as "the browser is done displaying them", so measuring their size is not yet possible.

So what I need is some way to tell when the new elements are actually there, so that I can reliably detect their widths and heights. I don't think document.ready() would help in this case, since the elements are added by a function that is called in document.load().

This is the code I copied from another stackoverflow thread (by a dude named Pointy, thanks by the way!)

function called_by_document_load() {
  var promises = [];
  for (var i=0; i<my_pics.length; i++) {
    (function(url, promise) {
      var img = new Image();
      img.onload = function () {
        promise.resolve();
      };
      img.src = url;
    }) (my_pics[i]['url'], promises[i] = $.Deferred());
  }
  $.when.apply($, promises).done(function() {
    // add the new images using append()
    // when done, call my own layout function
  }

... with the array my_pics containing the URLs of the images to load.

Any ideas? :-)

Thanks in advance,

snorri

snorri
  • 55
  • 7
  • `to random, weird behavior` Could you describe it more precisely? Beware than jQuery promise implementation (fixed in 3.x) is quite 'buggy'. If you want to hanlde onload event for dynamic image, you should instead capture the event using plain js method, if IE8 support is not needed. ***Edit:*** see if it fix your issue: http://stackoverflow.com/a/32395463/1414562 – A. Wolff Jan 22 '16 at 10:29
  • you can use the $.load() function, and fix in the callback function for each img. Link for ref -> http://api.jquery.com/load/ – Anas Bouhtouch Jan 22 '16 at 10:33
  • You could possibly add a timeout with a loop which keeps on checking until the DOM finds an element which has been appended and then calls the completion function. – Gerrit Brink Jan 22 '16 at 10:51

1 Answers1

1

After adding the new images using append, yield the control to the browser so that the browser can update the ui and then invoke your layout function. You can do so my using a setTimeout with delay set to 0.

$.when.apply($, promises).done(function() {
    // add the new images using append()

    setTimeout(function () {
        // when done, call my own layout function  
    }, 0);
}

There are many SO threads that explain why this works and is useful.

Here is one good explanation

Also, worth watching is the video by Philip Roberts on browser's event loop.

Community
  • 1
  • 1
Prashant
  • 7,340
  • 2
  • 25
  • 24
  • That's what OP is already using if i'm correct and not a great fix, but an easy one for sure – A. Wolff Jan 22 '16 at 10:28
  • I am curious of the way you can let the browser's render catch up without yielding with setTimeout(fn, 0). – Prashant Jan 22 '16 at 10:32
  • It all depends what exactly means OP by: `so that I can reliably detect their widths and heights`? NaturalWidth/height or what. Your solution could be the one but i'm really not sure about it – A. Wolff Jan 22 '16 at 10:34
  • Hi Prashant Palikhe, thanks for not only solving my problem, but also enlightening me about how and why this works. Awesome answer! :-) – snorri Jan 25 '16 at 10:51