Traversal and Chaining

This section documents jQuery 3.x (the current major line — no specific patch version is pinned). This content was generated with the assistance of AI and should be verified against the official jQuery API reference before being relied on in production, since API details and deprecations continue to change between releases.

jQuery is legacy-leaning: modern browsers implement native equivalents for almost everything it does (querySelectorAll, classList, fetch, addEventListener, append/prepend/remove, the Web Animations API), and most current stacks — Bootstrap 5 included — have dropped it. Prefer the native APIs for new work; this reference is aimed at reading, maintaining, or incrementally migrating code that already uses jQuery. Every page ends with a Modern equivalent note pointing at the native replacement.

This section’s bibliography lists the reference material consulted while preparing these pages.

Once you have a wrapped set, traversal methods return a new set relative to it — down to descendants, up to ancestors, or sideways to siblings — without going back through $(). Every traversal method returns a jQuery object, so calls chain.

Traversing the tree

Downward

$('#list').find('a')        // all <a> descendants, any depth
$('#list').children()       // direct children only
$('#list').children('.done')// direct children matching a selector

Upward

$('span.tag').parent()              // immediate parent (one level)
$('span.tag').parents()             // all ancestors up to <html>
$('span.tag').parents('.panel')     // ancestors filtered by selector
$('span.tag').parentsUntil('.panel')// ancestors, stopping before .panel
$('span.tag').closest('.panel')     // nearest ancestor matching .panel (incl. self)

.closest() walks up starting from each element itself and stops at the first match — the usual tool for "which card was clicked?" inside a delegated handler. .parents() always returns every ancestor and never includes the element itself.

Sideways

$('li.current').siblings()          // all siblings (not the element itself)
$('li.current').next()              // immediately following sibling
$('li.current').nextAll()           // all following siblings
$('li.current').nextUntil('.stop')  // following siblings up to .stop
$('li.current').prev()              // immediately preceding sibling
$('li.current').prevAll()           // all preceding siblings
$('li.current').prevUntil('.stop')  // preceding siblings up to .stop
graph TD root["ul#list"] a["li.a"] b["li.b (start here)"] c["li.c"] b1["span.tag"] b2["em"] root --> a root --> b root --> c b --> b1 b --> b2 classDef start fill:#cfe4fb,stroke:#2f6fa8,stroke-width:2px; classDef anc fill:#fdf1dc,stroke:#c9861f; classDef desc fill:#d3ead6,stroke:#3f8f4f; classDef sib fill:#f6d9a4,stroke:#8a5a10; class b start; class root anc; class b1,b2 desc; class a,c sib;

From li.b: .parent() is ul#list (ancestor, gold); .children() / .find() reach span.tag and em (descendants, green); .siblings() is li.a and li.c (siblings, tan).

Filtering a set

These narrow an existing set instead of moving in the tree:

$('li').filter('.active')       // keep only those matching
$('li').filter((i, el) => i % 2 === 0)  // keep by predicate (index, element)
$('li').not('.active')          // drop those matching
$('li').is('.active')           // boolean: does ANY element match?
$('li').has('a')                // keep those containing a matching descendant
$('li').eq(0)                   // the element at an index, as a set
$('li').first()                 // first, as a set  (prefer over :first)
$('li').last()                  // last, as a set
$('li').slice(1, 3)             // a sub-range, as a set

Iterating

$('li').each(function (index, element) {
  // `this` === element (a raw DOM node); wrap it to use jQuery methods
  $(this).attr('data-pos', index);
});

const texts = $('li').map(function (index, element) {
  return $(this).text().trim();   // return undefined/null to skip an item
}).get();                         // .get() converts the jQuery set to a plain array

In .each() and .map(), this is the raw element and the callback receives (index, element) — the opposite order from Array.prototype.forEach’s `(element, index).

Chaining and the destructive-operation stack

Because traversal and most manipulation methods return a jQuery set, calls compose in one statement:

$('#list')
  .find('li')          // set A: the <li> descendants
  .addClass('row')
  .filter('.done')     // set B: only the done ones
  .css('opacity', 0.5)
  .end()               // pop back to set A (all <li> again)
  .removeClass('row');

jQuery keeps an internal stack of prior sets. A method that narrows the set (.find, .filter, .children, .eq, …​) pushes the old set; .end() pops it, so the chain continues from where it branched. .addBack() merges the previous set back in instead of replacing:

$('#item').nextAll().addBack().addClass('highlight');
// highlights #item AND all of its following siblings

Without .end() / .addBack(), a chain can only ever get more specific.

Modern equivalent

el.closest(sel), el.querySelectorAll(sel), el.children, el.parentElement, el.nextElementSibling / previousElementSibling cover tree movement natively. There is no built-in sibling-list or "until" helper — build one with a small loop. Iterate a NodeList with for…​of; map with Array.from(list, fn). See Web programming basics.