This article is all about writing better jQuery code by using a technique called method chaining, made possible by jQuery’s fluent interface. So, yes, the title of this article is mostly a bad pun (sorry).
Fluent Interwhat?
jQuery’s fluent interface is best conveyed with an example. For starters, here’s a snippet of code that is not fluent.
var $basket = $('.basket');
$basket.show();
$basket.addClass('open');
$basket.attr('data-total', 0);
And here’s the snippet rewritten to be fluent.
$('.basket')
.show()
.addClass('open')
.attr('data-total', 0);
I could have made the whole thing one line, but it’s common to indent this way for readability (common mistake warning: only the last line ends with a semi-colon). Either way, it’s definitely less typing than the non-fluent equivalent, which is nice. The non-fluent code created an intermediate $basket variable to avoid executing the $('.basket')
selector multiple times, but this extra step isn’t necessary with method chaining.
Another big win is that the fluent code is more DRY (don’t repeat yourself). Imagine you’re doing some refactoring and decide that you want a “cart” instead of a “basket”. You’d need to change 5 instances of “basket” in the non-fluent code, but only a single instance in the fluent code.
Fancier Chaining
We’ve got basic method chaining covered, but jQuery can do more. Take this sample chunk of HTML.
<div class="basket">
<div class="egg"></div>
<div class="egg"></div>
<div class="egg"></div>
</div>
<div class="egg"></div>
jQuery allows you to chain methods across a changing selection set. The following code does some stuff to the basket element, then selects the egg elements within and adds a “boiled” class to them. You probably noticed in the HTML that I didn’t put all my eggs in one basket (sorry), but the method chaining makes it easy to properly exclude that outer egg.
$('.basket')
.show()
.addClass('open')
.attr('data-total', 0)
.find('.egg')
.addClass('boiled');
When chaining like this, jQuery actually keeps an in-memory stack of selection sets. Selection altering methods like .find()
push one onto the stack, while the .end()
method pops one off, allowing you to recall the previous selection set (“go back one”, so to speak).
Building on the previous code snippet, the following reduces the set of eggs to just the first one and gives it the “alpha-egg” class, then uses .end()
to return to the set of eggs, then finds the last egg and gives it the “omega-egg” class.
$('.basket')
.show()
.addClass('open')
.attr('data-total', 0)
.find('.egg')
.addClass('boiled')
.first()
.addClass('alpha-egg')
.end()
.last()
.addClass('omega-egg');
The code is reasonably readable thanks to good indentation, but it may be a bit… eggcessive (sorry). Chaining is a good to a point, but it’s up to you (or your team) to decide where to draw the line.
With that in mind, here’s one last chaining trick. You can use .addBack()
to combine the current selection set with the one before it. For example, this code performs various operations on the basket element and the egg elements within it, in one fell swoop.
$('.basket').find('.egg').addBack()
.show()
.addClass('open')
.attr('data-total', 0);
Bonus Tips
There are cases where chaining is possible, but a better approach exists. For example, you could add multiple classes to an element fluently, like this.
$('.egg')
.addClass('incredible')
.addClass('edible');
But there’s a better way.
$('.egg').addClass('incredible edible');
Another example is binding multiple event handlers.
$('.egg')
.on('click', crackEgg)
.on('dblclick', smashEgg);
Which can be rewritten to this.
$('.egg').on({
'click': crackEgg,
'dblclick': smashEgg
});
That same syntax also works for other jQuery methods like .css()
, .attr()
, and .prop()
.
Conclusion
Method chaining can help you write shorter, more elegant code. It’s a great tool to have, but like so many other things in programming, it’s good to know when to use it and when to use something else.