JavaScript course menu
Selecting DOM Elements: querySelector, querySelectorAll and getElementById
Learn how to find elements in JavaScript: querySelector and querySelectorAll with CSS selectors, getElementById, NodeLists and looping over them, searching inside an element, closest() and matches(), and navigating parents, children and siblings.
On this page
Before you can change an element, you need a reference to it. Modern JavaScript mainly uses two methods that accept CSS selectors, so everything you know from CSS Selectors applies.
querySelector: the first match#
document.querySelector(selector) returns the first element that matches, or null if nothing matches:
<h2 class="title">Courses</h2>
<ul id="courses">
<li class="course">HTML</li>
<li class="course featured">CSS</li>
<li class="course">JavaScript</li>
</ul>
<p id="out"></p>const list = document.querySelector("#courses"); // by id
const firstCourse = document.querySelector(".course"); // first by class
const featured = document.querySelector("li.featured"); // element + class
const missing = document.querySelector(".does-not-exist");
document.querySelector("#out").textContent =
`${list.tagName} | ${firstCourse.textContent} | ${featured.textContent} | ${missing}`;The result line shows UL | HTML | CSS | null. Always check for null when an element might not exist, or JavaScript will throw an error when you use it.
querySelectorAll: every match#
document.querySelectorAll(selector) returns a NodeList of all matches (empty if there are none):
<ul>
<li class="course">HTML</li>
<li class="course">CSS</li>
<li class="course">JavaScript</li>
</ul>
<p id="out"></p>const courses = document.querySelectorAll(".course");
courses.forEach((li, i) => {
li.textContent = `${i + 1}. ${li.textContent}`;
});
const names = [...courses].map((li) => li.textContent);
document.querySelector("#out").textContent = `${courses.length} courses: ${names.join(", ")}`;A NodeList has length and forEach, and works with for...of, but it is not an array: to use map or filter, convert it with [...nodeList] or Array.from(nodeList).
getElementById#
document.getElementById("courses") finds an element by id (without the #). It is slightly faster and you will see it a lot in existing code; querySelector("#courses") does the same job.
Older methods such as getElementsByClassName and getElementsByTagName return live collections that update automatically when the DOM changes, which can cause surprises in loops. Prefer querySelectorAll, whose results do not change.
Searching inside an element#
Every element also has querySelector and querySelectorAll, which only search inside it:
<div class="card" id="card-1"><h3>HTML</h3><button>Enrol</button></div>
<div class="card" id="card-2"><h3>CSS</h3><button>Enrol</button></div>
<p id="out"></p>const card2 = document.querySelector("#card-2");
const heading = card2.querySelector("h3"); // only searches inside card-2
document.querySelector("#out").textContent = `Second card is about ${heading.textContent}`;This keeps selectors short and avoids grabbing the wrong element when the same structure repeats.
Going up: closest and matches#
element.closest(selector) walks up the tree (starting with the element itself) and returns the nearest ancestor that matches. It is invaluable in event handlers:
<div class="card" data-course="HTML"><h3>HTML</h3><button>Enrol</button></div>
<div class="card" data-course="CSS"><h3>CSS</h3><button>Enrol</button></div>
<p id="out">Click a button.</p>document.querySelectorAll(".card button").forEach((button) => {
button.addEventListener("click", () => {
const card = button.closest(".card");
document.querySelector("#out").textContent = `You enrolled in ${card.dataset.course}`;
});
});element.matches(selector) returns true or false depending on whether the element itself matches a selector.
Moving around the tree#
Properties without "Element" in their names (childNodes, firstChild, nextSibling) also include text nodes such as the whitespace between tags, which is rarely what you want.
Practice#
Practice exercise
Given a list of tasks, make every task with the class done grey and struck through, and show how many tasks are left (not done) in a paragraph.
Show solution
<ul id="tasks">
<li class="done">Buy milk</li>
<li>Finish homework</li>
<li class="done">Call the bank</li>
<li>Water the plants</li>
</ul>
<p id="left"></p>document.querySelectorAll("#tasks .done").forEach((li) => {
li.style.color = "grey";
li.style.textDecoration = "line-through";
});
const left = document.querySelectorAll("#tasks li:not(.done)").length;
document.querySelector("#left").textContent = `${left} tasks left`;Key takeaway#
Finished this lesson?