Javascript / jQuery - how to call a function upon completion of the previous function

I use the following Javascript functions to display image galleries.

function disp_pics(currObj,table){ if(currObj != "none"){ $("div .checkout2").removeClass("checkout2").addClass("checkout"); $(currObj).closest("div").removeClass("checkout").addClass("checkout2"); } function getData(table){ return $.ajax({ url: "newphoto_gallery_display.php", type: "GET", data: { table: table }, dataType: "html" }); } function display_result(data){ var dfd = new $.Deferred(); dfd.done(equalise); $("#ajaxoutput").html(data); setTimeout(function() { dfd.resolve(); }, 1000); } function equalise(){ var highestBox = 0; $('.photodisplay').each(function(){ if($(this).height() > highestBox) { highestBox = $(this).height(); } }); $('.photodisplay').height(highestBox); } var promise=getData(table); promise.success(function (data) { promise.done(display_result(data)); }); }; 

The getData function retrieves image data from a database. The display_result function then outputs this data to the div id "ajaxoutput". Images are displayed together with the corresponding data in blocks (HTML tables with a frame). Then equalizes the function to make all fields equal height.

Without a time delay in display_result, the alignment function is called before all images and data are displayed and, therefore, ruins the display. Is there a way to end the time delay and only call the alignment function when display_result has finished outputting all the data in the ajaxoutput div?

I tried all kinds of deferred and $. when ....... then .... combinations, but could not achieve the desired result without delaying the script, which is not ideal. Any suggestions are welcome.

+5
source share
3 answers

If the problem is that you need to know when all the images that were applied to the HTML #ajaxoutput (and therefore their size is known to the browser) were uploaded, you can do this like this by monitoring onload for each of the images added to HTML:

 function display_result(data){ var dfd = new $.Deferred(); var imgWaiting = 0; $("#ajaxoutput").html(data).find("img").each(function() { if (!this.complete) { // img is still loading, install a load handler so we know when it done ++imgWaiting; this.onload = this.onerror = function() { --imgWaiting; if (imgWaiting === 0) { dfd.resolve(); } } } }); if (imgWaiting === 0) { dfd.resolve(); } return dfd.promise(); } 

Then you can do:

 return getData(table).then(display_result).then(equalise); 

This is a classic design pattern with promises for serializing multiple asynchronous operations.

+5
source

You need to get rid of the timeout delay, since it is not guaranteed long enough to download images (slow server, slow Internet, etc.).

Unfortunately, the β€œright” solution you were looking for is not the easiest because you can only access the img nodes, which after requesting $("#ajaxoutput").html(data) put them in their place . In the next statement, it will be unclear (due to browser behavior) whether the images will already be downloaded (from the cache) or not. Therefore, simply connecting the onload handler would be unreliable.

The workaround is to create a bunch of off-screen img nodes that reflect those in "#ajaxoutput" and promise them in such a way that their src properties are set after attaching onload (and onerror).

The code is not easy to follow, so I did my best to give explanatory comments.

 function display_result(data) { //First a function that creates a promisified <img> node, and returns its promise. //<img> nodes created here are not appended to the DOM. function imgPromise(index, imgNode) { return $.Deferred(function(dfrd) { //Note the order here - the onload/onerror handler is attached before the src attribute is set. //Note also that both onload/onerror cause the deferred to be resolved, as you don't want any failures to prevent calling `equalise()`. $("<img/>").on('load error', dfrd.resolve).attr('src', imgNode.src); }).promise(); } //A lot happens in the next line ... // * "#ajaxoutput" is given some HTML // * any freshly created image nodes in "#ajaxoutput" are discovered // * `imgPromise()` is called for each img node in turn and the returned promises are stuffed into an array // * the array is assigned to local var `promises`. var promises = $("#ajaxoutput").html(data).find("img").map(imgPromise); //Finally, `$.when()` returns a promise that is resolved when all `promises` have settled. //This "summary promise" is returned. return $.when.apply(null, promises); } 

Without comment you will see that it is actually very concise and hopefully a little less scary.

 function display_result(data) { function imgPromise(index, imgNode) { return $.Deferred(function(dfrd) { $("<img/>").on('load error', dfrd.resolve).attr('src', imgNode.src); }).promise(); } var promises = $("#ajaxoutput").html(data).find("img").map(imgPromise); return $.when.apply(null, promises); } 

Call the following:

 getData(table).then(display_result).then(equalise); 
+2
source

Can you run them synchronously?

 function display_result(data){ $("#ajaxoutput").html(data); equalise(); } 

I am sure that the .html function is a synchronous call, so after that there will be a resize.

Edited: What does the data coming from your ajax call look like? Is this just a bunch of img tags? If so, you are probably better off loading the image in ajax and yourself encoding it in html.

-1
source

Source: https://habr.com/ru/post/1209271/


All Articles