Iterating over jQuery and non-jQuery Objects
jQuery provides an object iterator utility called $.each()
as well as a jQuery collection iterator: .each()
. These are not interchangeable. In addition, there are a couple of helpful methods called $.map()
and .map()
that can shortcut one of our common iteration use cases.
$.each()
$.each
is a generic iterator function for looping over object, arrays, and array-like objects. Plain objects are iterated via their named properties while arrays and array-like objects are iterated via their indices.
$.each()
is essentially a drop-in replacement of a traditional for
or 'for-in' loop. Given:
1
2
3
|
|
then this:
1
2
3
4
5
|
|
can be replaced with this:
1
2
3
4
5
|
|
Notice that arr[ index ]
can't be accessed as the value is conveniently passed to the callback in $.each()
. In addition, given:
1
2
3
4
5
|
|
then this:
1
2
3
4
5
|
|
can be replaced with this:
1
2
3
4
5
|
|
Again, obj[ key ]
is passed directly to the callback and thus can't be accessed. Note that $.each()
is for plain objects, arrays, array-like objects that are not jQuery collections.
This would be considered incorrect:
1
2
3
4
|
|
For jQuery collections, use .each()
.
.each()
.each()
is used directly on a jQuery collection. It iterates over each matched element in the collection and performs a callback on that object. The index of the current element within the collection is passed as an argument to the callback. The value (the DOM element in this case) is also passed, but the callback is fired within the context of the current matched element so the this
keyword points to the current element as expected in other jQuery callbacks.
For example, given the following markup:
1
2
3
4
5
|
|
.each()
may be used like so:
1
2
3
4
5
6
7
8
|
|
The Second Argument
The question is often raised, "If this
is the element, why is there a second DOM element argument passed to the callback?"
Whether intentional or inadvert, the execution context may change. When consistently using the keyword this
, it's easy to end up confusing ourselves or other developers reading the code. Even if the execution context remains the same, it may be more readable to use the second parameter as a named parameter. For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
|
Sometimes .each()
Isn't Necessary
Many jQuery methods implicitly iterate over the entire collection, applying their behavior to each matched element. For example, this is unnecessary:
1
2
3
|
|
and this is fine:
1
|
|
Each <li/>
in the document will have the class 'newClass' added.
On the other hand, some methods do not iterate over the collection. .each()
is required when we need to get information from the element before setting a new value.
This will not work:
1
2
3
4
|
|
Rather, this is how it should be written:
1
2
3
4
|
|
The following is a list of methods that require .each()
:
.attr()
(getter).css()
(getter).data()
(getter).height()
(getter).html()
(getter)-
.innerHeight()
-
.innerWidth()
.offset()
(getter)-
.outerHeight()
-
.outerWidth()
-
.position()
.prop()
(getter).scrollLeft()
(getter).scrollTop()
(getter).val()
(getter).width()
(getter)
Note that in most cases, the 'getter' signature returns the result from the first element in a jQuery collection while the setter acts over the entire collection of matched elements. The exception to this is .text()
where the getter signature will return a concatenated string of text from all matched elements.
In addition to a setter value, the attribute, property, css setters, and DOM insertion 'setter' methods (i.e. .text()
and .html()
) accept anonymous callback functions that are applied to each element in the matching set. The arguments passed to the callback are the index of the matched element within the set and the result of the 'getter' signature of the method.
For example, these are equivalent:
1
2
3
4
5
6
7
8
9
|
|
One other thing to keep in mind with this implicit iteration is that traversal methods such as .children()
or .parent()
will act on each matched element in a connection, returning a combined collection of all children or parent nodes.
.map()
There is a common iteration use case that can be better handled by using the .map()
method. Anytime we want to create an array or concatenated string based on all matched elements in our jQuery selector, we're better served using .map()
.
For example instead of doing this:
1
2
3
4
5
|
|
We can do this:
1
2
3
|
|
Notice the .get()
chained at the end. .map()
actually returns a jQuery-wrapped collection, even if we return strings out of the callback. We need to use the argument-less version of .get()
in order to return a basic JavaScript array that we can work with. To concatenate into a string, we can chain the plain JS .join()
array method after .get()
.
$.map
Like $.each()
and .each()
, there is a $.map()
as well as .map()
. The difference is also very similar to both .each
methods. $.map()
works on plain JavaScript arrays while .map()
works on jQuery element collections. Because it's working on a plain array, $.map()
returns a plain array and .get()
does not need to be called — in fact, it will throw an error as it's not a native JavaScript method.
A word of warning: $.map()
switches the order of callback arguments. This was done in order to match the native JavaScript .map()
method made available in ECMAScript 5.
For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
|