learn.colinkim.dev

Selecting elements

Learn how to find specific elements in the DOM using query selectors, and the difference between selecting one element and selecting many.

Before JavaScript can change an element, it needs to find it. The DOM provides several methods for selecting elements, all based on CSS selectors.

querySelector selects one element

document.querySelector() returns the first element that matches a CSS selector:

const heading = document.querySelector("h1");
const menuItem = document.querySelector(".nav li:first-child");
const submitBtn = document.querySelector("#submit");

If no element matches, it returns null:

const missing = document.querySelector(".nonexistent");
console.log(missing);  // null

querySelectorAll selects all matches

document.querySelectorAll() returns a NodeList of all matching elements:

const paragraphs = document.querySelectorAll("p");
const activeItems = document.querySelectorAll(".nav li.active");

The NodeList is array-like — it has .length and .forEach(), but it is not an actual array:

const items = document.querySelectorAll(".item");

items.forEach((item) => {
  console.log(item.textContent);
});

// Convert to a real array to use array methods
const itemsArray = Array.from(items);
const firstThree = itemsArray.slice(0, 3);

Legacy selection methods

Older code uses these methods. They still work but querySelector and querySelectorAll are preferred:

document.getElementById("submit");       // single element by ID
document.getElementsByClassName("nav");  // live HTMLCollection by class
document.getElementsByTagName("p");      // live HTMLCollection by tag name

These return live collections that update when the DOM changes. This can cause bugs during iteration:

const items = document.getElementsByTagName("li");

for (let i = 0; i < items.length; i++) {
  items[i].remove();  // removes the element — items.length changes, items shift
}
// Some items are skipped because the collection is live

Common selector patterns

// By ID — fastest, returns one element
document.querySelector("#username");

// By class — returns all elements with this class
document.querySelectorAll(".card");

// By attribute
document.querySelector('[type="email"]');
document.querySelectorAll('[data-active="true"]');

// Descendant selector
document.querySelector("form input[name='password']");

// Direct child selector
document.querySelectorAll("ul > li");

// Pseudo-selectors
document.querySelector("li:first-child");
document.querySelector("li:last-child");
document.querySelector("li:nth-child(2)");

Checking whether an element exists

Always check for null before using a selected element:

const modal = document.querySelector(".modal");

if (modal) {
  modal.classList.add("visible");
}

Accessing a property on null throws an error:

document.querySelector(".missing").textContent;
// TypeError: Cannot read properties of null (reading 'textContent')

Selection scope

You can call querySelector on any element, not just document. This scopes the selection to descendants of that element:

const card = document.querySelector(".card");

if (card) {
  const title = card.querySelector("h2");       // h2 inside this card only
  const button = card.querySelector("button");   // button inside this card only
}

This is essential when working with multiple similar elements — each card has its own heading and button.

What to carry forward

  • querySelector(selector) returns the first matching element or null
  • querySelectorAll(selector) returns a static NodeList of all matches
  • NodeLists are array-like — use .forEach() or convert to array with Array.from()
  • always check for null before using a selected element
  • querySelector works on any element, not just document — use it to scope searches
  • prefer querySelector/querySelectorAll over legacy methods

Selection is the first step in DOM manipulation. The next lesson covers how to change what elements display.

Progress

Quick checks

No quick checks in this lesson.

Mark lesson manually or answer quick checks to track progress.