map() get() confusion

Fundamentals

There are two different jQuery map() functions: .map(), and $.map(). They perform similar things, but over different collections. You’re using the first form, which does the following:

  1. Iterate over the jQuery object (collection, whatever) on which the function was invoked. In this case, that’s $(this), which is whatever the .equalizeHeights() function was invoked on …which is $('div'): all <div> elements on the page (phew).
  2. Create an array with the same number of elements as the object that .map() was invoked on (all divs on the page, remember) whose nth element is generated by invoking the provided callback – I’ll get there in a sec – on the nth element in the targeted jQuery object. In this particular case, that callback is this function:

    function(i, e) {
    return $(e).height();
    }

Yes, .map() does look like .each(), but there is a key difference:

  • .each() performs an action on each of the elements in the collection; the return value of the callback passed to .each() is used to determine whether or not the iteration continues.
  • .map() also performs an action on each of the elements in the collection, but the callback’s return value is used to generate an element in the array-like object returned by .map().

Are you still with me?

jQuery obects are array-like, but they are not arrays! The reason that you call .get() at the end of the .map() call is to turn that jQuery object into a true array. The elements of that array are the values returned by the callback.

Putting it all together

This function sets the height of every single <div> on the page to the height of the tallest <div>. Here’s how:

$('input').click(function() {   // bind a click listener to every <input> element
    $('div').equalizeHeights(); // ...that will call the equalizeHeights() fn
                                //    on all <div> elements when fired
});

So, looking inside of the equalizeHeights() definition:

$.fn.equalizeHeights = function() {
    // construct an array that contains the height of every <div> element
    var two = $(this).map(function(i, e) {
                                return $(e).height();
                          });


    return this.height(    // set the height of element <div> element to...
        Math.max.apply(    // the largest value in...
            this,two.get() // the array of height values
        )
    ); // ...and finally, return the original jQuery object to enable chaining
}

But what about the constructor business?

As you discovered, yes, one is an object (a jQuery object) and the other is an array. That’s why you need that .get() call to turn the array-like object into something that Math.max() can understand.

Instead of looking at the constructor property, you can use a little more jQuery to figure out just what you’re looking at:

console.log(two.jquery);         // the version of jquery, something like "1.4.4"
console.log($.isArray(two));     // is it a plain old JS array? false
console.log(two.get().jquery);   // undefined! it's just an array.
console.log($.isArray(two.get()));    // true

Even better is to look at the actual objects inside of a debugger, rather than just console.log()-ing them. That way, you can see the entire object graph, all its properties, etc.

Any questions? Comment away.

Leave a Comment