Fun With JavaScript and jQuery Loops

When writing your loops with jQuery, it’s suggested to you to use the following:

$('.some-elements').each(function(){ 
    ... logic .... 
});

But depending on how many elements jQuery selects, the looping can actually be very slow. Because you are wrapping an additional procedure of asking jQuery to perform a function called .each, to which this function then performs a standard JavaScript for loop, we are slowing things down by not going straight to the source.

Another popular one is for Arrays in jQuery:

$.each(someArray,function(){ 
    ... logic ... 
});

Same problem here as well. You have a predefined array of values or objects (someArray) that you are asking a jQuery function called .each to loop over for you, instead of just writing it our yourself.

So, from here on out in this article, we will be ditching these looping methods in jQuery for traditional JavaScript methods of looping elements, but do so while still in the contexts of a jQuery environment.

The for loop method

for(var i = 0; i < someLength; i++){
    ... logic ...
}

This for loop is what jQuery is doing behind the scenes when you use the $(‘.some-elements’).each method. In the above example, “someLength” would be replaced with the number of elements that “$(‘.some-elements’)” would find; so we could write our actual code as follows.

var someElms = $('.some-elements');
for(var i = 0; i < someElms.length; i++){
    ... logic ...
}

If you want to target, condition or modify the elements in our variable “someElms”, you would do so in our loop, like so:

var someElms = $('.some-elements');
for(var i = 0; i < someElms.length; i++){
    $(someElms[i]).addClass('new-class');
}

We are basically stating, that the element in “someElms” variable that has an array index number of “[i]” (the numbers we are looping through), is now going to be a jQuery object. We wrap the found element (someElms[i]) in a jQuery object ($(someElms[i])) so that we can use the jQuery method of addClass, or any jquery method for that matter. Remember, our goal is just to make a faster loop, not destroy jQuery all together.

The above examples of the for loop are looping in one direction, “up” if you will. You can however make it loop backwards and gain a more of performance for huge sets of needed data to be looped, like so:

var someElms = $('.some-elements');
for(var i = someElms.length; i--;){
    $(someElms[i]).addClass('new-class');
}

So here, we are saying that our variable of “i” is equal to the length of someElems, and thus we deplete “i’s” length with each loop through the use of “i–;” (please note the semicolon is important for backwards for loops to work).

Why is this method faster you ask? It has to deal with the fact that we’re not asking three questions out of each loop, but only two. In our first examples of the for loop, we setup our counting variable (i), ask if “i” was still less than our found element’s length, and then increase i with each loop. When you loop backwards however, you only need to ask our counting variable if it is equal to our found element’s length, so minus this number each loop; no more asking if a number is less than anything.

The while loop method

Another, seemly faster way to loop over elements is the while loop:

var someElms = $('.some-elements');
var i = 0;
while(i < someElms.length){
    ... logic ...
    i++;
}

The while loop is a bit primitive in the fact that is requires you to maintain your variable counts, and really just aims and providing you with a fast raw loop. Once in the logic portion, same rules apply in how to target elements in “someElems” and wrap them in jQuery object to use jQuery methods.

Like the backwards for loops, while loops can also gain in performance by looping backwards too.

var someElms = $('.some-elements');
var i = someElms.length;
while(i--){
    ... logic ...
}

Again, this is faster because we are not asking JavaScript to evaluate anything through each loop, but rather just deduct a number from our count and continue along with our looping.

If you choose to use backwards looping I applaud you, but you must be mindful of one tiny drawback to using backwards looping. The largest drawback to using backwards looping is well, you loop backwards, and thus any storage of the data you are looping over will result in being stored backwards as well. Take for instance, we want to traverse over a list of links and build an array of their urls, like so:

var anchors = $('#list').find('a');
var hrefs = [];
var i = anchors.length;
while(i--){
    hrefs.push($(anchors[i]).attr('href'));
}

Then you might notice that the urls will be indexed in “hrefs” backwards (last to first). This can be a problem if say you want to then loop of “hrefs” and produce a second list of links, because the list will be backwards. So, we have two ways to deal with this, one being to simply change the storage procedure type of our “hrefs” array from push(), to unshift(). unshift() will store things into an array from back to front, so if we happen to already be looping backwards, then unshift() is equivalent to .push() when looping forward.

The reverse method

Great, but what about the scenario where you have a need to flip an array around in order before looping it? Well, you may think to just loop over it forwards, but that means we loose the performance gain of backwards looping. To flip an array prior to looping, we uses the .reverse() method on our array prior to the loop, and thus ensuring that the loop will loop over our array in any order we want, like so:

var anchors = $('#list').find('a').reverse();
var hrefs = [];
var i = anchors.length;
while(i--){
    hrefs.push($(anchors[i]).attr('href'));
}

Notice that we store our selector of links to the variable “anchors”, but we store it in a reverse() method? Now, before you get all crazy with reverse(), you must know that the above code snip will fail to work with jQuery, because jQuery has not built out a namespace function under the jQuery object for reverse. reverse() is a ECMA function, not a jQuery function and thus you can’t use the .reverse() method with any of the jQuery selection methods… unless you add this guy anywhere in your code:

jQuery.fn.reverse = [].reverse;

This basically extends out the ECMA reverse() method to any jQuery object as a jQuery method, or function if you will.

To conclude, stop using jQuery looping if you are serious about writing JavaScript under performance killing requirements, and consider taking a look at using backwards looping methods to really gain speed out of your JavaScript functionality.

Thanks, and enjoy.

Devin R. Olsen

Located in Portland Oregon. I like to teach, share and dabble deep into the digital dark arts of web and game development.

More Posts

Follow Me:
TwitterFacebookGoogle Plus