Chaining Internals & the Stack
See why chaining works, and how .end() rewinds a traversal to keep chains alive.
Chaining feels like magic, but it rests on one plain rule: most jQuery methods return a jQuery object, so the next method has something to act on. Setters and effects return the same set; traversal methods return a new set.
The internal stack
When you traverse with .find(), .filter(), .children() and friends, jQuery keeps a reference to the previous set on an internal stack. Two methods let you move along it:
.end()— pop back to the previous set in the chain..addBack()— add the previous set back into the current one.
$("#menu")
.find("li") // now working on the <li>s
.addClass("item")
.end() // rewind back to #menu
.addClass("ready"); // acts on #menu againWithout .end(), that final .addClass would land on the <li> set, not the menu.
Getters break the chain
Methods that return a value — .text() with no argument, .val(), .attr("x"), .is(), .index() — hand back a string, boolean, or number, not a jQuery object. The chain ends there. Recognising getter-vs-setter at a glance is what stops the classic “.text(...) is not a function” mistake.
| Returns the set (chainable) | Returns a value (chain ends) |
|---|---|
.css("color", "red") | .css("color") |
.addClass("x") | .hasClass("x") |
.find("li") | .length |
Example
When to use it
- A plugin calls .find('.child'), applies styles, then calls .end() to return to the original parent and hide it in one chain.
- A test helper chains .filter(':visible'), asserts the count, and calls .end() to get back to the full set for the next assertion.
- A builder script calls .children(), modifies them, and ends with .end().fadeIn() to animate the parent after updating its children.
More examples
Use .end() to rewind to original set
.end() pops the internal traversal stack, rewinding the matched set to what it was before the last .filter() call.
<ul id="menu">
<li class="active">Home</li>
<li>About</li>
<li>Contact</li>
</ul>
<script>
$(function () {
$("#menu li")
.filter(".active")
.css("font-weight", "bold") // acts on .active only
.end() // back to all li
.css("padding", "4px 8px"); // applies to all li
});
</script>Chain find, modify, end, and act on parent
.find(".badge") enters the sub-set, styles the badge, then .end() returns to #card so .fadeIn() acts on the container.
<div id="card" style="display:none;padding:16px;background:#eef;">
<span class="badge">New</span>
<p>Card content here.</p>
</div>
<button id="show">Reveal card</button>
<script>
$(function () {
$("#show").click(function () {
$("#card")
.find(".badge")
.css({ background: "#e74c3c", color: "#fff", padding: "2px 6px" })
.end()
.fadeIn(400);
});
});
</script>addBack to include the original element
.addBack() merges the previous matched set back into the current one so the style applies to both #box and its <p> child.
<div id="box" style="padding:10px;border:1px solid #ccc;">
<p>Paragraph inside box.</p>
</div>
<button id="outline">Outline all</button>
<script>
$(function () {
$("#outline").click(function () {
$("#box")
.find("p")
.addBack() // includes #box itself in the set
.css("outline", "2px dashed steelblue");
});
});
</script>
Discussion