Selectors
Selecting the right elements is the first step of almost every jQuery statement. The good news: jQuery uses CSS selectors, the same patterns you write in stylesheets. If you know CSS, you already know most jQuery selectors.
In this lesson you will practise the selectors you will use every day, meet the few extra selectors that only jQuery understands, and see how to do the same thing with document.querySelectorAll().
CSS selectors work as they are
Anything you can write in a stylesheet you can pass to $(). Here are the most common patterns, with their plain JavaScript twins:
| Selector | Matches | jQuery | Plain JavaScript |
|---|---|---|---|
#cart | the element with id cart | $("#cart") | document.querySelector("#cart") |
.item | every element with class item | $(".item") | document.querySelectorAll(".item") |
ul li | every <li> inside a <ul> (any depth) | $("ul li") | document.querySelectorAll("ul li") |
ul > li | <li> that are direct children | $("ul > li") | document.querySelectorAll("ul > li") |
a[href^="https"] | links whose href starts with https | $('a[href^="https"]') | document.querySelectorAll('a[href^="https"]') |
input:checked | ticked checkboxes and radios | $("input:checked") | document.querySelectorAll("input:checked") |
li:not(.done) | list items without class done | $("li:not(.done)") | document.querySelectorAll("li:not(.done)") |
The one real difference is the return value. $() always returns a jQuery set. querySelector() returns one element or null, and querySelectorAll() returns a static NodeList, which you loop over with forEach.
Try the selectors on a small class list. Each button highlights a different selection:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Selector practice</title>
<style>
.pick { background: #ffe58a; }
li { padding: 0.15rem 0.4rem; }
</style>
</head>
<body>
<h2>Class 10-B</h2>
<ul id="students">
<li data-city="Rohtak" class="topper">Aarav</li>
<li data-city="Jaipur">Priya</li>
<li data-city="Rohtak">Kabir</li>
<li data-city="Kochi" class="topper">Meera</li>
<li data-city="Pune">Rohan</li>
</ul>
<p>
<button data-sel=".topper">.topper</button>
<button data-sel='[data-city="Rohtak"]'>[data-city="Rohtak"]</button>
<button data-sel="li:not(.topper)">li:not(.topper)</button>
<button data-sel="li:nth-child(odd)">li:nth-child(odd)</button>
</p>
<p id="out">Click a selector.</p>
<script src="https://cdn.jsdelivr.net/npm/jquery@4.0.0/dist/jquery.min.js"></script>
<script>
$("button").on("click", function () {
const selector = $(this).attr("data-sel");
$("#students li").removeClass("pick"); // clear the last result
const $found = $("#students").find(selector).addClass("pick");
$("#out").text(selector + " matched " + $found.length + " student(s)");
});
</script>
</body>
</html>$(this) inside the handler wraps the clicked button, and .find(selector) searches only inside #students, so the buttons themselves are never matched.
Searching inside an element
Searching the whole page is wasteful when you know where to look. Start from a container and use .find():
const $menu = $("#main-menu");
const $links = $menu.find("a"); // only links inside the menu
const $links2 = $("a", "#main-menu"); // same result: a second "context" argument
// Plain JavaScript: any element has querySelectorAll
const menu = document.querySelector("#main-menu");
const links = menu.querySelectorAll("a");Prefer the .find() form. It reads left to right and makes the starting point obvious.
Selectors that only jQuery understands
jQuery adds some extension selectors that are not part of CSS. You will see them in older code:
| jQuery extension | Meaning | Plain JavaScript idea |
|---|---|---|
:visible / :hidden | takes up space on the page or not | el.checkVisibility() or check offsetParent / getClientRects().length |
:contains("text") | text includes the given words | filter by el.textContent.includes(...) |
:input | input, select, textarea, button | "input, select, textarea, button" |
:checkbox, :radio, :text | inputs of that type | input[type="checkbox"] and so on |
:header | h1 to h6 | "h1, h2, h3, h4, h5, h6" |
:animated | currently running a jQuery animation | no direct equivalent |
These work in jQuery 4, but they have a cost: because the browser does not understand them, jQuery cannot hand the whole selector to the fast built-in querySelectorAll() and must check elements itself. Where you can, select with plain CSS first and then narrow down with a method such as .filter().
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Extension selectors</title>
<style>.mark { outline: 2px solid #0769ad; }</style>
</head>
<body>
<ul id="menu">
<li>Masala dosa</li>
<li>Paneer dosa</li>
<li hidden>Cheese dosa (sold out)</li>
<li>Idli sambar</li>
</ul>
<p id="out"></p>
<script src="https://cdn.jsdelivr.net/npm/jquery@4.0.0/dist/jquery.min.js"></script>
<script>
// jQuery extensions
const $dosas = $("#menu li:contains('dosa'):visible").addClass("mark");
// Plain JavaScript: real CSS, then filter the array yourself
const dosas = [...document.querySelectorAll("#menu li")]
.filter((li) => li.textContent.includes("dosa") && li.checkVisibility());
$("#out").text("jQuery found " + $dosas.length + ", plain JS found " + dosas.length + " visible dosas.");
</script>
</body>
</html>Both approaches find two dosas: the hidden "Cheese dosa" is skipped. Note that :contains() is case-sensitive, just like includes().
Special characters in ids and classes
Characters such as ., #, : and [ have meaning in selectors. An id like user.name would be read as "id user with class name". Escape it first:
const id = "user.name";
$("#" + $.escapeSelector(id)); // jQuery 3.0 and later
document.querySelector("#" + CSS.escape(id)); // the browser's built-in version
document.getElementById(id); // no escaping needed at allCommon mistakes
- Forgetting the
#or..$("cart")looks for a<cart>element, not idcart. - Searching the whole page repeatedly. Store a container in a variable and call
.find()on it. - Building selectors from user input without escaping. Unusual characters break the selector or match the wrong elements.
- Relying on
:visiblein big loops. It forces the browser to calculate layout. Keep a class such asis-hiddenand select that instead.
Key points
- jQuery accepts any CSS selector;
querySelectorAll()accepts the same selectors natively. $()always returns a set;querySelector()can returnnull.- Use
.find()on a stored container to search a smaller part of the page. - Extension selectors (
:visible,:contains(),:input) are jQuery-only and slower; positional ones (:first,:eq()) are deprecated. - Escape unusual ids with
$.escapeSelector()orCSS.escape().
Exercise
Create a list of five cricket players, each with a data-role attribute of batter, bowler or all-rounder. Add two buttons: the first highlights all bowlers using an attribute selector with jQuery; the second highlights every player who is not a batter using plain querySelectorAll with :not().
Show solution
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Team roles</title>
<style>.pick { background: #ffe58a; }</style>
</head>
<body>
<ul id="team">
<li data-role="batter">Ananya</li>
<li data-role="bowler">Farhan</li>
<li data-role="all-rounder">Isha</li>
<li data-role="bowler">Rohan</li>
<li data-role="batter">Kabir</li>
</ul>
<button id="bowlers">Bowlers</button>
<button id="not-batters">Not batters</button>
<script src="https://cdn.jsdelivr.net/npm/jquery@4.0.0/dist/jquery.min.js"></script>
<script>
$("#bowlers").on("click", function () {
$("#team li").removeClass("pick");
$('#team li[data-role="bowler"]').addClass("pick");
});
document.querySelector("#not-batters").addEventListener("click", () => {
const all = document.querySelectorAll("#team li");
all.forEach((li) => li.classList.remove("pick"));
document.querySelectorAll('#team li:not([data-role="batter"])')
.forEach((li) => li.classList.add("pick"));
});
</script>
</body>
</html>The same CSS selector text works in both styles; only the way you act on the result differs.
Quick Quiz
1. Which selector finds only the <li> elements that are direct children of #menu?
The > combinator matches direct children only; a space matches descendants at any depth.
2. Why can $("li:visible") be slower than $("li.shown")?
Non-standard selectors make jQuery check elements itself and calculate layout.
3. What should you write instead of the deprecated $("tr:even")?
jQuery 3.5 added the .even() and .odd() methods to replace the positional selectors.
4. An element has id="price.total". Which call selects it correctly?
Escaping turns the dot into a literal character instead of a class selector.
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
