The $ Function & Chaining
Every jQuery program is built from one function, $, and the jQuery object it returns. Once you understand what that object is, why one method call can change fifty elements, and why some calls can be chained while others cannot, the rest of jQuery is just a list of methods.
This lesson also shows the plain JavaScript equivalents, because the biggest difference between the two styles is right here: jQuery works on sets of elements, while the DOM works on one element at a time.
What you can pass to $()
$ (or its full name jQuery) does different jobs depending on what you give it:
$("li.done"); // 1. a CSS selector: find matching elements
$("<li>Buy milk</li>"); // 2. an HTML string: create new elements (not yet on the page)
$(document.body); // 3. a DOM element (or an array of them): wrap it
$(function () { /* ... */ }); // 4. a function: run it when the DOM is readyIn all of the first three cases you get back a jQuery object. In the plain DOM, these are different functions: document.querySelectorAll() for selecting, document.createElement() for creating, and you use elements directly without wrapping.
The jQuery object is a set
A jQuery object is an array-like list of DOM elements: it has a length and numbered positions, plus all the jQuery methods. It is never null, even when nothing matched.
const $items = $("#list li");
$items.length; // how many elements matched (0 if none)
$items[0]; // the first DOM element itself (a plain <li>)
$items.get(0); // the same thing
$items.eq(0); // the first element, still wrapped in a jQuery object
$($items[0]); // wrap a DOM element to use jQuery methods on it againMany developers start variable names that hold jQuery objects with $, like $items, so they can tell at a glance that it is a jQuery set and not a DOM element. It is only a naming habit, not a rule.
Implicit iteration
When you call a setter (a method that changes something) on a set, jQuery applies it to every element in the set. You do not write a loop. This is called implicit iteration.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Implicit iteration</title>
<style>.sale { color: #b3261e; font-weight: bold; }</style>
</head>
<body>
<h2>Farhan's Stationery, Pune</h2>
<ul>
<li class="price">Notebook: ₹60</li>
<li class="price">Geometry box: ₹185</li>
<li class="price">Fountain pen: ₹240</li>
</ul>
<button id="jq">Mark sale (jQuery)</button>
<button id="js">Remove sale (plain JS)</button>
<script src="https://cdn.jsdelivr.net/npm/jquery@4.0.0/dist/jquery.min.js"></script>
<script>
// jQuery: one call changes all three items
$("#jq").on("click", function () {
$(".price").addClass("sale");
});
// Plain JavaScript: you loop yourself
document.querySelector("#js").addEventListener("click", () => {
document.querySelectorAll(".price").forEach((li) => li.classList.remove("sale"));
});
</script>
</body>
</html>querySelectorAll returns a NodeList, which has forEach but no classList, so you must loop. That loop is the main reason plain DOM code is a little longer.
Getters and setters
Most jQuery methods do two jobs. Call them with no value and they get (read); call them with a value and they set (write):
| Call | Kind | Returns |
|---|---|---|
$("h1").text() | getter | a string |
$("h1").text("Hi") | setter | the same jQuery object |
$("input").val() | getter | the value of the first input |
$("input").val("") | setter | the same jQuery object, every input emptied |
Two rules follow from this table:
- Getters usually read only the first element.
.val(),.attr()and.css()look at the first match. (.text()is the exception: it joins the text of all matched elements.) - Setters return the jQuery object, so you can call another method on the result. This is chaining.
Chaining
Because setters return the same set, you can write several steps in one statement. Put each step on its own line for readability:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Chaining</title>
<style>
.card { border: 1px solid #ccc; padding: 0.75rem; border-radius: 8px; max-width: 18rem; }
.ready { border-color: #2e7d32; background: #edf7ee; }
</style>
</head>
<body>
<div class="card" id="order">Order #1042: packing</div>
<p id="info"></p>
<button id="ship">Mark as shipped</button>
<script src="https://cdn.jsdelivr.net/npm/jquery@4.0.0/dist/jquery.min.js"></script>
<script>
$("#ship").on("click", function () {
const $card = $("#order")
.text("Order #1042: shipped to Jaipur")
.addClass("ready")
.attr("data-status", "shipped");
// A getter ends the chain because it returns a value, not the set
const status = $card.attr("data-status");
$("#info").text("Status saved as: " + status);
});
</script>
</body>
</html>Plain DOM setters are mostly properties (el.textContent = "...") or methods that return undefined (classList.add()), so they cannot be chained. You write one statement per change instead, usually on a variable that holds the element.
Looping with .each()
When each element needs different treatment, use .each(). The callback receives the index and the DOM element, and this is also the current DOM element:
$(".price").each(function (index, element) {
const rupees = Number($(element).text().split("₹")[1]);
$(element).toggleClass("expensive", rupees > 200);
console.log(index, rupees);
});
// Plain JavaScript
document.querySelectorAll(".price").forEach((element, index) => {
const rupees = Number(element.textContent.split("₹")[1]);
element.classList.toggle("expensive", rupees > 200);
console.log(index, rupees);
});Notice the argument order: jQuery's .each() gives (index, element), while forEach gives (element, index). Mixing them up is a classic bug when converting code.
If you use an arrow function with .each(), this will not be the element (arrow functions do not get their own this). Use the element parameter instead.
Common mistakes
- Checking
if ($("#x"))to see whether an element exists. A jQuery object is always truthy, even when empty. Check$("#x").lengthinstead. - Calling jQuery methods on
[0]. Indexing gives a plain DOM element. Use.eq()or wrap it with$(). - Expecting a getter to read every element.
$("input").val()returns only the first input's value. - Using
thisinside an arrow function with.each()or.on(). Use the element parameter or a normalfunction.
Key points
$()selects, creates, wraps or registers a ready handler, depending on its argument.- A jQuery object is an array-like set of DOM elements;
.lengthtells you how many matched. - Setters apply to every element (implicit iteration) and return the set, so they can be chained.
- Getters usually read only the first element and return a value, which ends the chain.
.each((index, element) => ...)loops when elements need different handling; plainforEachuses(element, index).
Exercise
Make a list of four train names with the class train. With jQuery, add the class checked to all of them in one call, then use .each() to append the position number to each name, for example "1. Shatabdi Express". Finally show the total count in a paragraph using .length.
Show solution
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Trains</title>
<style>.checked { color: #2e7d32; }</style>
</head>
<body>
<ul>
<li class="train">Shatabdi Express</li>
<li class="train">Rajdhani Express</li>
<li class="train">Vande Bharat Express</li>
<li class="train">Duronto Express</li>
</ul>
<p id="count"></p>
<script src="https://cdn.jsdelivr.net/npm/jquery@4.0.0/dist/jquery.min.js"></script>
<script>
const $trains = $(".train").addClass("checked");
$trains.each(function (index, element) {
$(element).text(index + 1 + ". " + $(element).text());
});
$("#count").text($trains.length + " trains listed");
</script>
</body>
</html>addClass changes every item at once, while .each() is needed because each item gets a different number.
Quick Quiz
1. What does $("#missing").length return when no element has that id?
A jQuery object always exists; when nothing matches, its length is 0.
2. What does $("p")[0] give you?
Square-bracket indexing returns the raw DOM element; use .eq(0) to keep a jQuery object.
3. Why can you write $("h1").text("Hi").addClass("big")?
Returning the set from setters is what makes chaining possible.
4. What is the argument order of the callback in jQuery's .each()?
jQuery passes the index first; the native forEach passes the element first.
Learn jQuery with a mentor
Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
