Skip to content
Laviyx Learn

Try

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.

Intermediate6 min readUpdated
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:

HTML
<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>
JavaScript
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}`;
Result

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):

HTML
<ul>
  <li class="course">HTML</li>
  <li class="course">CSS</li>
  <li class="course">JavaScript</li>
</ul>
<p id="out"></p>
JavaScript
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(", ")}`;
Result

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:

HTML
<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>
JavaScript
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}`;
Result

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:

HTML
<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>
JavaScript
document.querySelectorAll(".card button").forEach((button) => {
  button.addEventListener("click", () => {
    const card = button.closest(".card");
    document.querySelector("#out").textContent = `You enrolled in ${card.dataset.course}`;
  });
});
Result

element.matches(selector) returns true or false depending on whether the element itself matches a selector.

Moving around the tree#

PropertyGives
parentElementThe parent element
childrenThe child elements (an HTMLCollection)
firstElementChild, lastElementChildFirst and last child elements
nextElementSibling, previousElementSiblingThe neighbouring elements

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
HTML
<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>
JavaScript
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`;
Result

Key takeaway#

Finished this lesson?