0

I'm using jQuery to load text from files into divs using load().

There is a JavaScript function I need to call only when that text has loaded. I know I can use the load() callback to do this once one of the text files has loaded, but how to I do this only once all the files have loaded?

This is my attempt using $.when():

$.when($("#testdiv").load("text/textures.txt")).done(functio‌​n () { var msg = document.getElementById('testdiv').innerHTML; alert(msg); });

There is a comment below that points to why it is wrong. I need to dig deeper to understand it.

Pang
  • 9,564
  • 146
  • 81
  • 122
mrboni
  • 11
  • 3
  • Possible duplicate of [Callback after all asynchronous forEach callbacks are completed](http://stackoverflow.com/questions/18983138/callback-after-all-asynchronous-foreach-callbacks-are-completed) – Sinan Samet Nov 01 '16 at 10:28
  • Use [`$.when()`](http://api.jquery.com/jQuery.when/)? – Pang Nov 01 '16 at 10:29
  • I tried when() and the function called by when (an alert that displays the div contents) happened before the div had been updated – mrboni Nov 01 '16 at 13:13
  • `$.when($("#testdiv").load("text/textures.txt")).done(function () { var msg = document.getElementById('testdiv').innerHTML; alert(msg); });` – mrboni Nov 01 '16 at 13:17
  • I had a look at the link above - forEach callbacks - but it seems specific to processing items in a forEach loop, which I'm not doing – mrboni Nov 01 '16 at 13:23
  • [`$.when()`](http://api.jquery.com/jQuery.when/) accepts [deferred Object](http://api.jquery.com/category/deferred-object/) while [`.load()`](http://api.jquery.com/load/) returns `jQuery`, so your code is wrong. – Pang Nov 02 '16 at 00:40
  • Anyway, I suggest you to update your question to show how you're doing the `load()`. I'm sure others can more easily answer your question by working on top of your code. You can update your question by clicking on the **"[edit]"** link under the post. Thank you. – Pang Nov 02 '16 at 00:43
  • Thanks Pang. Code added above. Will investigate deferred objects – mrboni Nov 02 '16 at 14:04
  • Can you show how you're loading into multiple divs? Are you having multiple divs with the id `testdiv` or are you having another line of code for another div? Are you loading the same file into multiple divs or are you loading different files for each div? – Pang Nov 03 '16 at 01:53
  • Why not use the complete callback of load function? [here](http://api.jquery.com/load/) .. Edited: my bad, sorry. Readed your question again, and I saw your problem with the callback. – Kiritonito Nov 03 '16 at 02:08
  • How about set an interval timer to check if the divs are empty? If none is empty, clear the interval and fire the next function. – sideroxylon Nov 03 '16 at 02:34

2 Answers2

0
var loaded = 0;

function callback() {
    loaded++;
    if (loaded === totalNeeded) {
        alldone();
    }
}

function alldone() {
    //do stuff here;
}
Adrian Brand
  • 20,384
  • 4
  • 39
  • 60
  • This is not a good general solution, it can only handle simple scenarios where there is only one list of elements and the number is stored as a variable scoped to the whole code. It only partially answers the question and has no explanation. – doug65536 Nov 03 '16 at 09:09
0

You can extend jQuery to add the functionality you want. Here's an example of adding a jQuery method called loadPromise which takes either a string url like .load does, or a callback which is called for each matching element, which returns the argument to pass to .load for that element. It returns a single promise which resolves to an array with one .load result per element loaded.

// You can pass a single url to be used on all matching elements
// or you can pass a callback, which is called for each
// element in the selector, which returns the url to use
// for that element
$.fn.loadPromise = function(urlOrCallback) {
    var promises = [];
    this.each(function(i, element) {
        var url,
            defer;

        if (typeof urlOrCallback === 'function')
            url = urlOrCallback.call(element, i, element, this);
        else
            url = urlOrCallback;

        if (url) {
            defer = $.Deferred();
            $(element).load(url, function(responseText, textStatus, xhr) {
                defer.resolve({
                    element: element,
                    responseText: responseText,
                    textStatus: textStatus,
                    xhr: xhr
                });
            });
            promises.push(defer.promise());
        }
    });
    return $.when.apply($, promises).then(function(/*...*/) {
        return Array.prototype.slice.call(arguments);
    });
};

// Simple example:
$('.single-example')
.loadPromise('example-single')
.then(function(result) {
    console.log('done load: ' + result[0]);
});

// Example with multiple
$('.multi-example').loadPromise(function() {
    return $(this).attr('data-url');
}).then(function(multi) {
    console.log('multi done', multi);
});

Here's a live demo on jsfiddle. Note that since jsfiddle has gone to great lengths to block this sort of thing, it gets 404 errors (of course, the example names I pass aren't valid URLs), but if you check the console, you'll see it is working as intended. On your own site, it will have no trouble loading if your existing .load works.

How it works:

JQuery extension functions get the selector passed in this. It uses each to iterate through each matching element, calling .load for that element with the passed url or the result of calling the callback, and creates a promise that is resolved for each one and adding it to the promises array.

Once it has its array of promises, it takes that and uses apply to pass multiple parameters to $.when, one for each element. $.when returns a promise which is resolved when all of the promises are resolved. The .then for the promise $.when returns receives multiple parameters, one per promise. The .then handler there takes all of the parameters and makes one array from them all, and returns it, resolving the promise returned from loadPromise with a single parameter, an array of completions.

Run the fiddle and have a look at the debugger console. You'll see that the promise loadPromise returns is resolved with an array, click into them and have a look at what it returns. You probably don't need that much detailed information, just the promise being resolved at all tells you that they all finished loading.

Side note

Most jQuery APIs that can work on multiple elements have an overload where you can pass a function, and give it a value to use for an argument for each element, as I did in my extension. For example, .text can work on a selector that matches multiple elements, getting the text for each element by calling your callback. Apparently, they forgot to do this for .load. Additionally, jQuery has a mechanism for waiting for multiple completions, which is used for waiting for multiple animations. They seem to have forgotten about that with .load as well.

Community
  • 1
  • 1
doug65536
  • 6,562
  • 3
  • 43
  • 53