Reputation: 55678
I have an array of URLs of content to load asynchronously, e.g.
var toLoad = ['snippet1.html', 'snippet2.html', ... ];
I'm trying to load each snippet with AJAX and append it to a parent <div>
, maintaining the order of content in the array. Because of the way the rest of my code is set up (and for performance reasons), I don't want to load in a synchronous manner, either with async:false
or by chaining .ajax()
calls, as suggested in answers to this similar question. Instead, I'd like to put logic in the AJAX callback function to place loaded snippets correctly, based on the index.
The following code works:
var $parent = $('#mydiv');
toLoad.forEach(function(url, i) {
// load snippet
$.get(url, function(snippet) {
// attach order to DOM element
snippet = $(snippet).data('order', i);
// find the sibling with the next lowest order number
var $prev = $parent
.children()
.filter(function() {
return $(this).data('order') < i
})
.last();
if ($prev[0]) snippet.insertAfter($prev);
else $parent.prepend(snippet);
});
});
...but I don't like it. In particular, I don't like attaching the order to the DOM element with .data()
; this seems ugly, and I'd rather not stick this into the DOM if I can help it.
Is there a better way to achieve the goal of asynchronously loading content into a div with a predefined order?
Upvotes: 1
Views: 3909
Reputation: 318302
There's simple recursion, waiting until one is finished before the next one starts :
var toLoad = ['snippet1.html', 'snippet2.html', ....];
var $parent = $('#mydiv');
var i = 0;
getSnippet(toLoad[i]);
function getSnippet(url) {
$.get(url, function(snippet) {
$parent.append(snippet);
if (i<toLoad.length) getSnippet(toLoad[i++]);
});
}
then there's doing them all at once, just sticking them in an array, running the done()
functions one after the other to make the sure the elements are inserted in order:
var toLoad = ['snippet1.html', 'snippet2.html', 'snippet3.html'];
var $parent = $('#mydiv');
var gets = [];
toLoad.forEach(function(url, i) {
var XHR = $.get(url);
gets.push(XHR);
});
gets[0].done(function(snippet) {
$parent.append(snippet);
gets[1].done(function(snippet) {
$parent.append(snippet);
gets[2].done(function(snippet) {
$parent.append(snippet);
});
});
});
which could probably be done in some sort of loop if doing many ajax calls.
Then there's deffered objects and promises:
$.when($.get(toLoad[0]),
$.get(toLoad[1])
$.get(toLoad[2])
).done(function(value1, value2, value3) {
// value1 etc. are arguments resolved for the ajax requests, respectively
// arguments are [ "success", statusText, jqXHR ]
$parent.append(value1[2]+value2[2]+value3[2]);
}
});
Upvotes: 3
Reputation: 31823
If your main gripe is using .data(), you could maintain your own array which associates dom node elements to values.
var map = [
{order: 5, nodeReference: ...},
{order: 9, nodeReference: ...},
{order: 1, nodeReference: ...}
];
function data(node) {
// loop through map, comparing to nodeReference to get the order
}
//and then ...
.filter(function() {
return data(this) < i
})
your code mostly stays the same, except you need to add to the map when you append something to the dom.
I would try hard to continue to progressively add things to dom as soon as they load, like you do now. I wouldn't wait until everything is loaded, or even adding them in order. let the order work itself out eventually like you do now. this is how web browsers load images in web pages, and there's deep reasons for it.
Upvotes: 2