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.