jQuery .each() and $.each(): Looping Through Elements, Arrays, and Objects

How I use jQuery .each() and $.each() on elements, arrays and objects: break and continue, the this trap, the array-like object bug, closures, and when $.map, $.grep or no loop at all is the better choice.

Executive Summary: I use .each() to loop over a jQuery selection and $.each() to loop over arrays and objects. Both pass the index or key first, and both stop only when the callback returns exactly false. The traps I see are a raw this, a bare return that skips instead of stopping, and objects with a length key. Often you need no loop at all. Tested on jQuery 1.9.1.
jQuery each illustration: a clay conveyor ring carries coloured blocks past a small robotic arm that handles each block in turn

The question I get: “I want to do something to each item in a list. Do I use .each() or $.each()? Why does my loop not stop when I return? And why is this not a jQuery object?” Each part has a short, testable answer, and jQuery each covers all of them.

jQuery each comes in two forms. $(selector).each(fn) is a method that loops over the elements a selection matched. $.each(collection, fn) is a utility function that loops over any array or plain object. The official $.each documentation and the .each() documentation describe both. I ran every case against jQuery 1.9.1. The fixture is a four-item list and a four-row table:

<ul id="list"><li class="r">alpha</li><li class="r">beta</li><li class="r">gamma</li><li class="r">delta</li></ul>
<table id="t"><tr><td>1</td></tr><tr><td>2</td></tr><tr><td>3</td></tr><tr><td>4</td></tr></table>

When I skip jQuery each entirely

My position: the best each is the one you never write. jQuery methods already loop over every element in a selection, so $('li').addClass('x') needs no loop. I reach for .each() when each element needs different treatment. I reach for $.each() when I am looping over data rather than the page. If you need a refresher on building the selection itself, see jQuery selectors explained.

Looping over arrays and objects with jQuery each

var fruits = ['apple', 'banana', 'cherry'];
$.each(fruits, function (index, value) {
  console.log(index, value);
});

var user = { name: 'Ana', city: 'Pune', age: 29 };
$.each(user, function (key, value) {
  console.log(key + ' = ' + value);
});
0 apple
1 banana
2 cherry
name = Ana
city = Pune
age = 29

For an array, the callback receives (index, value). For an object, it receives (key, value). Note the order. The index comes first, the reverse of the native forEach method in newer browsers (value, index). Internet Explorer 8 has no native forEach at all. That is one reason so much code uses $.each: it works everywhere.

The same function handles data from the server. A parsed JSON response is just an object, so you can walk it directly:

$.getJSON('/json').done(function (data) {
  $.each(data, function (key, value) {
    console.log(key, '->', $.isArray(value) ? '[' + value.join(', ') + ']' : (typeof value === 'object' ? JSON.stringify(value) : value));
  });
  $.each(data.tags, function (i, tag) { console.log('tag', i, tag); });
});
id -> 7
name -> Ana
tags -> [a, b]
q -> {}
tag 0 a
tag 1 b

Looping over elements with .each()

$('#list li').each(function (index, element) {
  // this === element, a raw DOM node. Wrap it to get jQuery methods.
  var $li = $(this);
  console.log(index, element === this, $li.text(), $li.hasClass('r'));
  if (index % 2 === 0) $li.addClass('even');
});
console.log('even rows:', $('#list li.even').length);
0 true alpha true
1 true beta true
2 true gamma true
3 true delta true
even rows: 2

Inside the callback, this and the second argument are the same raw DOM element. The true in the second column shows it. A raw element has no jQuery methods, so wrap it as $(this) to call .text() or .addClass(). Using $(this) several times in one callback? Wrap it once into a variable (var $li = $(this)), because each wrap creates a new object.

The this trap with primitive values

$.each([10, 'x', true], function (i, value) {
  console.log(i, 'typeof this:', typeof this, '| typeof value:', typeof value, '| this === value:', this === value, '| this == value:', this == value);
});
0 typeof this: object | typeof value: number | this === value: false | this == value: true
1 typeof this: object | typeof value: string | this === value: false | this == value: true
2 typeof this: object | typeof value: boolean | this === value: false | this == value: true

Loop over an array of numbers, strings or booleans with $.each(), and this becomes a boxed object version of the value. It is loosely equal (==) to the value but never strictly equal (===). That breaks comparisons such as if (this === 'x'). Use the second argument, value, whenever you loop over data.

Stopping early: break, continue, and the bare return

$('#list li').each(function (i) {
  if (i === 1) return true;        // like "continue"
  if (i === 3) return false;       // like "break"
  console.log('visited', i, $(this).text());
});

var visited = [];
$.each([1, 2, 3, 4], function (i, n) {
  if (n === 2) return;             // a bare return is NOT a break
  visited.push(n);
});
console.log('bare return skips only that item:', visited.join(','));
visited 0 alpha
visited 2 gamma
bare return skips only that item: 1,3,4

You cannot use break or continue inside the callback, because it is a function, not a loop body. jQuery each uses return values instead. return false stops the loop, and any other return value moves on to the next item. The last line is the trap. A bare return; returns undefined, which is not false. So it acts like continue, and the loop carries on. Only the exact value false breaks out. Watch for a callback that ends with a comparison that evaluates to false. It will stop your loop without warning.

The array-like object bug

// An object with a numeric "length" AND matching index keys looks like an array to jQuery
var box = { length: 2, 0: 'first', 1: 'second', color: 'red' };
$.each(box, function (key, value) {
  console.log('$.each  ->', key, value);
});
for (var key in box) console.log('for...in ->', key);
$.each  -> 0 first
$.each  -> 1 second
for...in -> 0
for...in -> 1
for...in -> length
for...in -> color

This is the most surprising behaviour in the whole API. jQuery decides whether something is an array by looking for a numeric length property. A plain object with a length key and matching index keys gets looped as an array. $.each visited only 0 and 1 and skipped color. A native for…in loop saw all four keys. It bites when server data includes a field named length, such as a duration or a size. If you need every key, use for…in with a hasOwnProperty check.

A string is a related edge case. In this run, $.each('abc', …) threw a TypeError, and the wording differs between browsers. Split the string first:

try {
  $.each('abc', function (i, ch) { console.log(i, ch); });
} catch (e) { console.log(e.name + ':', e.message.slice(0, 60)); }
$.each('abc'.split(''), function (i, ch) { console.log(i, ch); });
TypeError: Cannot use 'in' operator to search for '2' in abc
0 a
1 b
2 c

Closures: why .each() fixes the loop-variable bug

var out = [];
for (var i = 0; i < 3; i++) {
  setTimeout(function () { out.push('for/var: ' + i); }, 0);
}
$.each([0, 1, 2], function (index) {
  setTimeout(function () { out.push('$.each: ' + index); }, 0);
});
setTimeout(function () { console.log(out.join('\n')); }, 50);
for/var: 3
for/var: 3
for/var: 3
$.each: 0
$.each: 1
$.each: 2

A plain for loop with var i shares one i across all passes. By the time the timeouts fire, it is already 3. Each call of an .each() callback gets its own index variable, so the values are 0, 1, 2. That is a real reason to prefer jQuery each when you create handlers or timers inside a loop.

$.map() and $.grep(): when you want a new array

var nums = [1, 2, 3, 4, 5];

var doubled = $.map(nums, function (n) { return n * 2; });
console.log('$.map  doubled      :', JSON.stringify(doubled));

var flat = $.map(nums.slice(0, 3), function (n) { return [n, n * 10]; });
console.log('$.map  flattens     :', JSON.stringify(flat));

var dropped = $.map(nums, function (n) { return n % 2 ? n : null; });
console.log('$.map  drops null   :', JSON.stringify(dropped));

var big = $.grep(nums, function (n) { return n > 2; });
console.log('$.grep n > 2        :', JSON.stringify(big));

var texts = $('#list li').map(function () { return $(this).text(); }).get();
console.log('.map().get() texts  :', JSON.stringify(texts));
console.log('.map() alone is a jQuery object:', $('#list li').map(function () { return 1; }) instanceof $);
$.map  doubled      : [2,4,6,8,10]
$.map  flattens     : [1,10,2,20,3,30]
$.map  drops null   : [1,3,5]
$.grep n > 2        : [3,4,5]
.map().get() texts  : ["alpha","beta","gamma","delta"]
.map() alone is a jQuery object: true

Use each to do something and map to build something. $.map returns a new array of the callback results. It flattens any array you return ([1, 10, 2, 20, …]). It also drops null and undefined results, so it can filter too. $.grep is the dedicated filter. Two gotchas apply. On a jQuery selection, .map() returns another jQuery object, so call .get() for a plain array. And the argument order differs. $.map(array, function (value, index)) gives the value first. $(sel).map(function (index, element)) gives the index first.

Do you need the loop at all?

// Most jQuery methods already loop for you
$('#list li').addClass('seen');
console.log('addClass on all  :', $('#list li.seen').length, 'of', $('#list li').length);

// Getters read from the FIRST element only (text() is the exception: it joins all)
$('#list li').each(function (i) { $(this).attr('data-n', i); });
console.log('attr() getter    :', $('#list li').attr('data-n'), '(first element only)');
console.log('text() getter    :', $('#list li').text(), '(all elements joined)');
console.log('each() + attr    :', $('#list li').map(function () { return $(this).attr('data-n'); }).get().join(','));
addClass on all  : 4 of 4
attr() getter    : 0 (first element only)
text() getter    : alphabetagammadelta (all elements joined)
each() + attr    : 0,1,2,3

Setters apply to every matched element without a loop, so addClass reached all four. Getters differ, and they confuse people. .attr() returned the value from the first element only, while .text() joined all of them. When you need a value from each element, use .map() with .get(), as the last line shows.

var total = 0;
$('#t tr').each(function (i) {
  var n = parseInt($(this).find('td').text(), 10);
  total += n;
  $(this).toggleClass('odd', i % 2 === 1);
});
console.log('sum of cells:', total, '| odd rows:', $('#t tr.odd').length);
sum of cells: 10 | odd rows: 2

The table example is a fair use of .each(). Each row needs its own index and its own number. For plain row striping, though, a CSS rule with tr:nth-child(odd) does the job in modern browsers with no JavaScript. You mainly need the jQuery version for Internet Explorer 8 and earlier, which lack that selector.

Troubleshooting jQuery each loops

Symptom Cause Fix
Loop does not stop on return A bare return is undefined, a continue return false
Loop stops unexpectedly Callback’s last expression returned false End with an explicit return true or no value
this === 'x' never true this is a boxed primitive Compare the value argument
.text() not a function on this this is a raw DOM node $(this).text()
Some object keys never visited Object has a numeric length key Use for…in with hasOwnProperty
TypeError looping over a string Strings are not supported str.split('')
.attr() returns one value for many elements Getters read the first element .map(…).get() or .each()

How I use jQuery each in real code

  • Rendering lists from data. $.each over a JSON array builds markup, and I insert it once. My comparison of jQuery append, prepend and html explains why once matters.
  • Per-element setup in plugins. A plugin returns this.each(function () { … }). That sets up every matched element and keeps the call chainable.
  • Collecting values. My form code uses .map() and .get() to gather the values of all checked boxes into an array.
  • Attaching handlers once, not per element. I do not loop to bind a handler on each row. I bind one delegated handler to the table instead, as I describe in jQuery event handling and delegation.

The loop bug I meet most in older code is the silent skip. A $.each over an API response misses half the fields. The record has a length key, because it describes a video or a file, and jQuery treats the whole object as an array. The second most common is a return; meant as a break, so the loop keeps going and overwrites a value it already found. Now, when a jQuery each loop misbehaves, I check those two things first: any length key in the data, and whether the callback returns exactly false.

Which loop I pick

  1. Does the method already work on every element? If yes, call it on the selection and skip the loop.
  2. Is it elements or data? Use .each() for elements and $.each() for arrays and objects.
  3. Am I building a new array? Use .map() or $.map().
  4. Am I filtering? Use $.grep(), or .filter() on elements.
  5. Is it a huge array in a hot path? A plain for loop avoids a function call per item.

When not to use .each()

  • To set the same value on every element. Setters already loop.
  • To bind a handler on every item. Delegate one handler from the parent.
  • To read one value. .first() or .eq(0) is clearer than looping and breaking.
  • Objects that may have a length key. Use for…in.

Common jQuery each mistakes

  • Using break or continue inside the callback. Use return false and return true.
  • Expecting return; to stop the loop. It only skips.
  • Calling jQuery methods on this without wrapping it. Use $(this).
  • Mixing up the argument order. $.each and .each give the index first. $.map gives the value first.
  • Forgetting .get() after .map(). You still have a jQuery object.
  • Re-wrapping this repeatedly. Cache it.

FAQ

What is the difference between $.each() and .each() in jQuery?

$(selector).each(fn) is a method that loops over the DOM elements in a jQuery selection. $.each(collection, fn) is a utility function that loops over any array or object. Both call the function with the index or key first and the value or element second.

How do I break out of a jQuery each loop?

Return exactly false from the callback. The break and continue keywords do not work there, because the callback is a function. Returning true, or nothing, skips to the next item, like continue.

How do I loop through an array or an object with jQuery?

Use $.each(data, function (indexOrKey, value) { … }). It works for arrays, plain objects and parsed JSON from an AJAX response. Watch out for objects with a numeric length key, because jQuery each treats them as arrays.

Why is $(this) needed inside .each()?

Inside the callback, this is the raw DOM element, and it has no jQuery methods. Wrapping it as $(this) gives you .text(), .addClass(), .find() and the rest. Store the wrapped object in a variable if you use it more than once.

What is the difference between .each() and .map() in jQuery?

.each() runs a function for its side effects and returns the original selection. .map() builds a new collection from what the function returns. Call .get() on it to get a plain array.

The loop methods are simple. Nearly all of their bugs come from four rules people forget. return false breaks, this needs wrapping, array-like objects are special, and setters already loop. My rule of thumb for jQuery each: if every element gets the same treatment, call the method on the selection and skip the loop.

Originally posted on 9 April 2013 and post last modified on 2 November 2026

Front-End Development JavaScript jQuery Loops

Share this article

Leave a Reply

Your email address will not be published. Required fields are marked *