10
X

Peeling Away the jQuery Wrapper and Finding an Array

read 20 comments

If you haven't poked around under the hood of jQuery, you might not be aware that when you pass the jQuery function an expression or DOM element it places these elements (or, possibly a single element) into an object, and then this object is returned so that it can be chained. Without getting into the details of chaining, the fundamental concept to remember is this:

Most jQuery methods return the jQuery object itself, which allows methods to be chained.

The interesting thing about the jQuery object is that while its datatype is an object, it has array-like characteristics:

Continue Reading Below
  • its property names (the ones that refer to DOM elements, at least) are numeric
  • it has a length property

Have you ever noticed, for example, a snippet of jQuery code that directly accesses a DOM element by using the bracket operator with the jQuery function?

JavaScript:
  1. $('div')[0]; // give me the first DOM element that the
  2. // jQuery function found

Now, the jQuery function returns an object, but it looks like we just treated it as an array. It can also be written like this:

JavaScript:
  1. $('div').get(0) // do the same, using a jQuery method

If this is confusing, let's see if we can clear it up with some code. Go to the jQuery.com homepage, open up Firebug using Firefox, and run the following line of code in the console.

JavaScript:
  1. $('div').toSource(); // toSource() is a method of all JavaScript objects

You should be looking at this in the console:

"({length:25, 0:{}, 1:{}, 2:{}, 3:{}, 4:{}, 5:{}, 6:{}, 7:{}, 8:{}, 9:{}, 10:{}, 11:{}, 12:{}, 13:{}, 14:{}, 15:{}, 16:{}, 17:{}, 18:{}, 19:{}, 20:{}, 21:{}, 22:{}, 23:{}, 24:{}, prevObject:{0:{}, length:1}})"

Looky there! The jQuery function is returning an object with a bunch of properties. If we tack on the bracket operator to the jQuery function, which returns an object, we can access the properties of that object because inside the object are properties with a numeric property name. Fascinating isn't it?

I know what you're thinking: this tutorial is about arrays, not objects. Right, so let's get to the array part. Since we now know that the jQuery function returns an object that only masquerades as a JavaScript array the question at hand is how do I get a true JavaScript array from a jQuery wrapper object. This is actually very simple. Most of you might not have been aware (if not, I showed you earlier) that $('div')[0] and $('div').get(0) return a reference to the first element in the jQuery wrapper object. But, the get() method actually has another purpose. It can quickly turn the DOM elements found in the jQuery wrapper into an array of DOM elements.

Let's return to jQuery.com and open up Firebug again. Inside the console run the following line of code.

JavaScript:
  1. $('div').get().toSource();

You should now be looking at this in the firebug console:

"[{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}]"

Essentially the .get() method placed all of the DOM elements inside the jQuery wrapper into an array and returned that array. So now you can use default array methods on your array of elements. For example:

JavaScript:
  1. $('div').get().toString; //returns the array as a string
  2. //this is a method of the array object

Additionally, since we now have an array we can write a statement like this:

JavaScript:
  1. $('div').get()[0].id; //returns the first div's id attribute value

There is something worth noting here that might not be obvious to those starting out with jQuery or JavaScript. Since we are now returning an array, the chain is broken. You can't chain any jQuery methods after using the .get() method.

But, heck, you have an array. Now you can use the utility functions that jQuery provides ($.each(), $.grep(), $.map(), $.inArray(), $.unique()), as well as core JavaScript methods and properties, to work on the array. I'm not going to demonstrate all of the jQuery utility functions that can be used on an array, but I do want to examine one of them here in this tutorial.

The $.each() utility function, not to be confused with the $('selector').each() method used to operate on a set of DOM elements in a jQuery wrapper, can be used to seamlessly iterate over an array or object. For example, let's say that you would like to store (or cache) a set of <li> elements in an array, because maybe you are going to remove them from the DOM temporarily but eventually will be placing them back on the page. To do this you could use the $.each() utility function to loop over an array and append each <li> element back to the page. In the code example below this is done on page load by converting and storing the jQuery wrapper of <li> elements to an array, and then looping over this array in order to place them back on the page.

JavaScript:
  1. <html>
  2. <head>
  3. <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js"></script>
  4. </head>
  5. <body>
  6.  
  7. <ul>
  8. <li>task 1</li>
  9. <li>task 2</li>
  10. <li>task 3</li>
  11. <li>task 4</li>
  12. <li>task 5</li>
  13. <li>task 6</li>
  14. <li>task 7</li>
  15. <li>task 8</li>
  16. </ul>
  17.  
  18. <script>
  19.  
  20. var arrayList = $('ul li').get();
  21.  
  22. $('ul').empty();
  23.  
  24. $.each(arrayList, function(){
  25. $('ul').append('<li><del>'+$(this).html() +'</del> complete</li>');
  26. });
  27.  
  28. </script>
  29. </body>
  30. </html>

Notice that I am forgoing the use of the .ready() event by simply placing my jQuery code before the closing body element.

So, hopefully it's obvious how extremely handy it can be at times to make an array out of a jQuery wrapper. In fact, once you are dealing with an array, you can use a handy plugin called Rich Array. This plugin provides the following additional utility functions for arrays. And yes, there is a bit of duplication here with some of the core utility functions.

$.richArray.in() //Checks whether an array contains some value
$.richArray.unique() //Produces the duplicate-free version of the array
$.richArray.diff() //Finds the difference between two arrays.
$.richArray.intersect() //Finds the intersection of two arrays
$.richArray.filter() //Applies filter to the array, using callback function
$.richArray.map() //Applies callback function for each element in the input array, and returns array of values that this function returned
$.richArray.sum() //Computes the sum of all array elements
$.richArray.product() //Calculates the production of all elements of the array
$.richArray.reduce() //Reduces the array. One-element arrays are turned into their unique element
$.richArray.compact() //Creates new version of array without null/undefined values
$.richArray.without() //Creates a new version of the array that doesn't contain the specified value


comment feed

20 comments

  1. Brian

    Maybe it's worth mentioning too that $.each can loop over any object with a for..in loop but it doesn't do that on a jQuery object only because it has a length property that indicates that it's an array-like object so the non-integer properties shouldn't be enumerated so a simple for loop is used instead of for..in, treating it like a simple array.

  2. It should be pointed out that .toSource() is not supported in Safari, Chrome, or IE. :)

  3. Brian REindel

    Hey Cody,

    I believe the following also works:

    var el = $("div");
    $( el[0] ).id;
    

    I'm not sure if there is a disadvantage over .get(0), but I figured I would throw it out there as a short alternative when you already have a reference to your element.

  4. Josh Powell

    Another thing to mention is that $.each isn't necessary when creating events on DOM objects, as jQuery automatically iterates over every DOM element in the jQuery collection and sets the event on all of them. Example:

    
    $('a').click(function() {
      //do stuff
    });

    instead of what I have seen some people new to jQuery do:

    
    $('a').each(function(){
       $(this).click(function() {
          //do stuff
      });
    });

    The same goes for almost every jQuery method: .css, .attr, .etc...

  5. @Josh, $.each() and $().each() are different functions (although the functionalities are similar).

    This is a great insides Cody, thanks. This basically combines easiness of jQuery selectors and performance of native browser traversing. This is will probably boost performance of some of my previous projects where I had a very long lists and tables.

    Thanks again,
    jQuery Lover :)

  6. Josh Powell

    Yeah, sorry, typo. Thanks for the catch. For those playing at home...

    $().each(function) iterates over the selected DOM elements and executes the function on each of them.

    $.each(array,function) iterates over an array and executes the function.

    In each (pun intended) case "this" will get you the current element of the array and in the $() case, it is the unextended DOM element so if you want access to jQuery functions on it, you will need to wrap it in a $(this).

  7. blafazer

    tl;dr

    you are aware that firebug won't display array with non-numerical (associative) arrays correctly? This has caused some headaches for some people in the past and there are enough debates on wether you should use associative arrays or objects in the first place...

    in mootools for example you could simply..... nevermind, i'm drifting off ;)

  8. toSource() is a Firefox extension -- it isn't part of any spec -- toString() is the only (similar) function that's in ECMA262

  9. kris

    Any idea on how to incorporate a "toggle all" feature into this? I've been trying to figure it out, but not very experienced with Javascript or Jquery.

  10. kris

    Wouldn't you know it - I worked on this for quite a while, and then an hour after posting my question above, i figured it out. My problem was that if you checked on one of the regular checkboxes, and then checked the "check all" one, it would toggle the class to off. Anyway, here it is (probably could be cleaned up, but like i said above, I'm not very experienced with this stuff. I just added this to the head section

    
    $(document).ready(function()
          {
          $('#trcheckall').filter(':has(:checkbox:checked)').end().click(function(event)
              {
    
                  //if user didn't click on the checkbox itself, check it
                  if (event.target.type !== 'checkbox')
                       {
                       $(':checkbox', this).trigger('click');
                     }
    
                  //if check all button is checked, add the class
                  if($('#all').is(':checked'))
                  {
                  $(':checkbox, tr').not('#trcheckall').attr('checked', $('#all').is(':checked')).addClass('selected');
                  }
                  //if it's been unchecked, remove the class
                  else
                  {
                  $(':checkbox, tr').not('#trcheckall').attr('checked', $('#all').is(':checked')).removeClass('selected');
                  }
              });
          });
    

    and gave the row with the checkall box id = trcheckall and the input id = all.

  11. beginner

    Why is this in the beginner's category? This is far too complicated for a beginner like me!

    Thanks for taking the time to write it, but I'm so foncused :$

  12. your code highlighter sucks balls. it's not trusting the html and converting line breaks to p's and different characters to their html codes. lame. i can't say much, because i don't have my own code highlighter and have to resort to using dpaste.com.

  13. JeffM

    @Cody: Thanks for this insight. You just spared me a considerable amount of experimentation - and time.
    @Karl: LMAO at that last response, man.

  14. Hi Cody,

    Great tutorial. Thanks.

    We've added you to the codebix programmer's blog aggreagator. Do have a look and send us your feedback.

    Regards

  15. Dakota

    Thanks for the info on .get(), Cody. Previously, I've just been adding a [0] to my jQuery objects, but certainly prefer a pure approach rather than a jQuery/JS frankenstein, where possible. Thanks again.

  16. thanks for making if clear this options require advanced jquery experience not to mention i'm still a newbie when it comes to scripting .I will manage this to put it to my site.
    http://bit.ly/scriptmethis

    thanks,
    chloe andrea
    follow me at facebook
    facebook.com/chloe

  17. Thalis Kalfigkopoulos

    It's also worth looking into the jquery.makeArray() method. Does exactly the same if you pass it an array-like object as an arg.

3 Pings

  1. [...] Peeling Away the jQuery Wrapper and Finding an Array (Cody Lindley) [...]

  2. Weekly Web Nuggets #44 : Code Monkey Labs

    [...] Peeling Away the jQuery Wrapper and Finding an Array: Cody Lindley takes a look at some the underpinnings of jQuery. [...]

  3. [...] Peeling Away the jQuery Wrapper and Finding an Array [...]

Sorry, but comments for this entry are now closed.