JavaScript DOM Selectors: getElementById, querySelector and More
Learn to select elements with getElementById, getElementsByClassName, getElementsByTagName, querySelector and querySelectorAll, and when to use each one.
What you will learn
Before JavaScript can change anything on a page, it must first find the element. This is called selecting an element. In this lesson you will learn the five main ways to select elements: getElementById, getElementsByClassName, getElementsByTagName, querySelector and querySelectorAll. You will learn what each one returns, how live and static collections are different, and which method to choose in real projects.
Why do we need to select elements?
Imagine you are in a big school and the principal wants to talk to someone. There are three ways to call them:
- By their roll number (unique to one student). This is like an
id. - By their class (many students share it). This is like a
class. - By their type (all students, all teachers). This is like a tag name.
JavaScript does the same with the page. The selecting methods give you a reference to the element, which you store in a variable. After that, you can change it, read it, or react to it.
const title = document.getElementById("page-title"); // find it
title.textContent = "Big Sale!"; // use it
The sample page
All examples in this lesson use this small shop page. It is also loaded in the Practice in Editor button, so you can try every line.
<h1 id="page-title">MyCodeNest Store</h1>
<ul id="menu">
<li><a href="#home" class="link active">Home</a></li>
<li><a href="#courses" class="link">Courses</a></li>
<li><a href="#contact" class="link">Contact</a></li>
</ul>
<div class="product-list">
<div class="product featured">
<h2>Pen</h2>
<p class="price">₹10</p>
</div>
<div class="product">
<h2>Notebook</h2>
<p class="price">₹60</p>
</div>
<div class="product sold-out">
<h2>Bag</h2>
<p class="price">₹800</p>
</div>
</div>
<form id="signup">
<input type="text" name="username" placeholder="Your name">
<input type="email" name="email" placeholder="Your email">
<button type="submit" class="btn">Join</button>
</form>
Some CSS makes the products look like cards, so you can see your changes clearly:
.product-list {
display: flex;
gap: 12px;
margin: 16px 0;
}
.product {
border: 1px solid #ccc;
border-radius: 8px;
padding: 8px 16px;
}
.price {
font-weight: bold;
}
1. getElementById
Finds the one element with the given id. It returns the element, or null if nothing matches.
const title = document.getElementById("page-title");
console.log(title); // <h1 id="page-title">MyCodeNest Store</h1>
console.log(title.textContent); // "MyCodeNest Store"
console.log(document.getElementById("missing")); // null
Things to remember:
- Write the id without the
#sign.getElementById("#page-title")returnsnull. - It works only on
document. You cannot writesomeDiv.getElementById(...). - An id must be unique. If two elements share an id (a mistake), you get the first one.
- Capital letters matter:
getElementById(notgetElementByID), and"Page-Title"is not"page-title".
Real use: the “Pay now” button, a modal, a form, a main container. Anything that exists once on a page.
2. getElementsByClassName
Finds all elements with a class. It returns an HTMLCollection, a list-like object (never null; an empty collection if nothing matches).
const products = document.getElementsByClassName("product");
console.log(products.length); // 3
console.log(products[0].textContent); // text of the Pen card
console.log(products[2]); // the Bag card
Write the class without the dot: "product", not ".product".
You can ask for elements that have several classes at once by separating them with spaces:
const featured = document.getElementsByClassName("product featured");
console.log(featured.length); // 1 (only the Pen has both classes)
An HTMLCollection is not an array. You can use length, an index like products[0], and for...of, but not forEach, map or filter:
for (const product of products) {
console.log(product.querySelector("h2").textContent); // Pen, Notebook, Bag
}
// products.forEach(...) // TypeError: products.forEach is not a function
Array.from(products).forEach(function (p) { // convert to a real array first
console.log(p.className);
});
A common mistake is treating the collection as one element:
document.getElementsByClassName("price").textContent; // undefined (a collection has no textContent)
document.getElementsByClassName("price")[0].textContent; // "₹10" (pick one item first)
3. getElementsByTagName
Finds all elements with a tag name. It also returns a live HTMLCollection.
const items = document.getElementsByTagName("li");
console.log(items.length); // 3
const inputs = document.getElementsByTagName("input");
console.log(inputs.length); // 2
const all = document.getElementsByTagName("*"); // every element on the page
console.log(all.length);
Tag names are not case sensitive in HTML ("li" and "LI" both work). Use it for general jobs, like counting all images or links:
console.log("Images on page:", document.getElementsByTagName("img").length);
console.log("Links on page:", document.getElementsByTagName("a").length);
Bonus: other old-style helpers
| Helper | Returns |
|---|---|
document.getElementsByName("email") |
All elements with name="email" (useful for radio buttons) |
document.forms |
All <form> elements |
document.images |
All <img> elements |
document.links |
All <a> and <area> elements with an href |
4. querySelector
This is the most flexible method. You give it any CSS selector, exactly as you would write in a CSS file, and it returns the first matching element (or null).
document.querySelector("#page-title"); // by id (use the # sign)
document.querySelector(".product"); // by class (use the dot): the Pen card
document.querySelector("h2"); // by tag: the first <h2>
Because it uses CSS selectors, you can be very specific:
| Selector | What it finds on the sample page |
|---|---|
"#menu" |
The <ul> with id menu |
"ul li a" |
The first link inside a list |
"a.active" |
The Home link (it has the class active) |
".product.featured .price" |
The price inside the featured product (₹10) |
".product:not(.sold-out)" |
The first product that is not sold out (Pen) |
".product:last-child" |
The Bag card (the last child of its parent) |
"input[name='email']" |
The email input |
"form button" |
The Join button |
Selectors are covered fully in the CSS lessons CSS Syntax and Selectors and CSS Selectors in Depth. Everything you learned there works here.
const emailInput = document.querySelector("input[name='email']");
emailInput.placeholder = "you@example.com";
const joinButton = document.querySelector("form button");
joinButton.textContent = "Join Free";
If nothing matches, querySelector returns null. Calling a property on null crashes your script, so for elements that may not exist, check first or use optional chaining:
const banner = document.querySelector(".banner");
if (banner) {
banner.textContent = "Sale!";
}
// or, shorter:
const text = document.querySelector(".banner")?.textContent ?? "No banner";
console.log(text); // "No banner"
Tip: If your selector is not valid CSS (for example
"..product"),querySelectorthrows aSyntaxError. If an id starts with a number or has special characters, write it withCSS.escape("1st-item"), or usegetElementById, which accepts any id text.
5. querySelectorAll
The same as querySelector, but it returns all matches in a NodeList (an empty NodeList if there are none, never null).
const prices = document.querySelectorAll(".price");
console.log(prices.length); // 3
prices.forEach(function (price) {
console.log(price.textContent); // ₹10, ₹60, ₹800
});
A NodeList is a bit friendlier than an HTMLCollection:
| You can use on a NodeList | You cannot use |
|---|---|
length, [index], item(i) |
map, filter, reduce, find |
forEach |
push, pop |
for...of, entries() |
To use array methods, convert it first with Array.from() or the spread syntax:
const prices = document.querySelectorAll(".price");
const numbers = Array.from(prices).map(function (p) {
return Number(p.textContent.replace("₹", ""));
});
console.log(numbers); // [10, 60, 800]
const expensive = [...prices].filter(function (p) {
return Number(p.textContent.replace("₹", "")) > 50;
});
console.log(expensive.length); // 2
Searching inside an element
All the methods above, except getElementById, also work on any element, not only on document. The search then happens only inside that element. This is called scoping, and it is how you handle repeated blocks like product cards.
document.querySelectorAll(".product").forEach(function (card) {
const name = card.querySelector("h2").textContent;
const price = card.querySelector(".price").textContent;
console.log(name + " costs " + price);
});
Output:
Pen costs ₹10
Notebook costs ₹60
Bag costs ₹800
If you had searched with document.querySelector(".price") inside the loop, every card would give you the Pen’s price. Searching inside card fixes that.
Live vs static collections
This is a favourite interview question, so let us make it clear.
- A live collection updates itself automatically when the page changes. (
getElementsByClassName,getElementsByTagName) - A static collection is a snapshot taken at the moment you call the method. It does not change later. (
querySelectorAll)
You can see the difference with this experiment:
const liveList = document.getElementsByClassName("product");
const staticList = document.querySelectorAll(".product");
console.log(liveList.length, staticList.length); // 3 3
// Remove all classes from the Bag card (it is no longer a "product")
document.querySelector(".sold-out").className = "";
console.log(liveList.length, staticList.length); // 2 3
The live list noticed the change and now has 2 items. The static list still remembers the old 3.
Neither is better. Live lists are surprising when you loop and change the page at the same time (you will see a bug caused by this in the traversal lesson). Static lists are easier to reason about, which is one reason many developers prefer querySelectorAll.
Good to know: Not every
NodeListis static. ThechildNodesproperty returns a live NodeList. Only the one fromquerySelectorAllis a snapshot.
Comparing the five methods
| Method | Finds | Returns | If nothing matches | Live? |
|---|---|---|---|---|
getElementById("x") |
One element by id | Element | null |
n/a |
getElementsByClassName("x") |
All with a class | HTMLCollection | Empty collection | Yes |
getElementsByTagName("x") |
All with a tag | HTMLCollection | Empty collection | Yes |
querySelector("css") |
The first match of any selector | Element | null |
n/a |
querySelectorAll("css") |
All matches of any selector | NodeList | Empty NodeList | No |
Which one should I use?
- Need one element by id? Use
getElementById("id")(it is simple and very fast), orquerySelector("#id"). Both are fine. - Need one element by anything else? Use
querySelector. - Need many elements? Use
querySelectorAll. - Reading old code or answering an interview question? Know all five, because the older ones are still everywhere.
In modern projects, querySelector and querySelectorAll handle almost everything. The speed difference between the methods is too small to matter for normal pages.
Real use cases
Calculate a cart total
const prices = document.querySelectorAll(".price");
let total = 0;
prices.forEach(function (price) {
total += Number(price.textContent.replace("₹", ""));
});
console.log("Total:", total); // Total: 870
Mark sold-out products and feature one
const soldOut = document.querySelector(".sold-out");
soldOut.style.opacity = "0.4";
document.querySelector(".product.featured").style.border = "2px solid gold";
Highlight the current menu link
const links = document.querySelectorAll("#menu .link");
links.forEach(function (link) {
link.style.color = "gray";
});
document.querySelector("#menu .active").style.color = "green";
A complete working page
This page counts the products that are in stock, shows the total price, and fades the sold-out product. It is a full page, so press Try it to run it.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Stock Summary</title>
<style>
body { font-family: sans-serif; padding: 20px; }
.product-list { display: flex; gap: 12px; margin-bottom: 16px; }
.product { border: 1px solid #ccc; border-radius: 8px; padding: 8px 16px; }
.price { font-weight: bold; }
.summary { padding: 12px; background: #eff6ff; border-radius: 8px; }
</style>
</head>
<body>
<div class="product-list">
<div class="product">
<h2>Pen</h2>
<p class="price">₹10</p>
</div>
<div class="product">
<h2>Notebook</h2>
<p class="price">₹60</p>
</div>
<div class="product sold-out">
<h2>Bag</h2>
<p class="price">₹800</p>
</div>
</div>
<p class="summary" id="summary"></p>
<script>
const cards = document.querySelectorAll(".product");
let inStock = 0;
let total = 0;
cards.forEach(function (card) {
if (card.matches(".sold-out")) {
card.style.opacity = "0.4";
return; // skip sold-out products
}
inStock++;
const priceText = card.querySelector(".price").textContent;
total += Number(priceText.replace("₹", ""));
});
document.getElementById("summary").textContent =
inStock + " products in stock. Total price: ₹" + total;
</script>
</body>
</html>
Here card.matches(".sold-out") asks “does this element match this selector?” and gives true or false. It is a handy helper that you will meet again in the traversal lesson.
Common mistakes
- Using
#or.with the old methods.getElementById("#title")andgetElementsByClassName(".product")return nothing. OnlyquerySelectorandquerySelectorAlluse#and.. - Forgetting that a collection is not one element.
getElementsByClassName("price").textContentisundefined. Use an index, or loop. - Calling
forEachon an HTMLCollection. It has noforEach. Usefor...of, orArray.from()first. - Using
querySelectorwhen you want all. It gives only the first match. - Not handling
null. If the element does not exist,querySelectorandgetElementByIdgivenull. Check it, or use?.. - Running the script before the element exists. Put the script at the end of
<body>, or usedefer(see the DOM introduction lesson). - Searching the whole page inside a loop. Use
card.querySelector(...)to search inside the current card. - Duplicate ids.
getElementByIdreturns only the first one. Use classes for repeated things. - Expecting
querySelectorAllto update itself. It is a snapshot. Select again after you add or remove elements.
Practice
- Select the page title with
getElementByIdand change its text withtextContent. - Use
getElementsByClassName("product")to print how many products there are, then print the name (h2) of each one withfor...of. - Use
getElementsByTagName("a")to count the links, andgetElementsByTagName("input")to count the inputs. - Use
querySelectorto select the email input and change itsplaceholder. - Use
querySelectorAll(".price")andforEachto add a"+ GST"text at the end of every price. (Hint:price.textContent += " + GST".) - Use
.product:not(.sold-out)withquerySelectorAllto count the products in stock. - Try
document.querySelector(".banner")and read the result. Then write code that changes the banner text only if the banner exists. - Create
liveListandstaticListas in the live vs static experiment, but this time give the first menu item the classproductwithdocument.querySelector("li").className = "product". Which list grows from 3 to 4, and which one stays at 3? - Challenge: with
querySelectorAll,Array.fromandmap, build an array of objects like{ name: "Pen", price: 10 }from the three product cards, and print it withconsole.table.
Recap
- Selecting means finding an element so you can change or read it. Every selecting method starts from
document(or another element). getElementById("id")returns one element ornull. No#sign.getElementsByClassName("x")andgetElementsByTagName("x")return a live HTMLCollection. It haslengthand indexes, but noforEachormap.querySelector("css")returns the first match ornull.querySelectorAll("css")returns a static NodeList of all matches. Both accept any CSS selector.- A NodeList has
forEach. To usemaporfilter, convert it withArray.from()or[...list]. - Call the methods on an element (for example
card.querySelector(".price")) to search only inside it. - A live collection changes with the page. A static one is a snapshot.
- For most work, use
querySelectorandquerySelectorAll. UsegetElementByIdfor ids. Always handle thenullcase. - Next you will learn DOM traversal: moving from one element to its parent, children and siblings.