Basic DOM Selection

Course: JavaScript Beginner

Lesson 13: Basic DOM Selection

Level

Beginner

Estimated Reading Time

10–15 minutes

Goal

By the end of this lesson, the learner will be able to:

  • Select elements from the page using querySelector and querySelectorAll
  • Understand what the DOM is and how JavaScript relates to the HTML document
  • Read element properties like textContent, value, and className

Frontend Development Context

Before JavaScript can do anything with a page element — change its text, show or hide it, read what a user typed — it first needs a reference to that element. DOM selection is how JavaScript gets that reference. Without it, your code is completely disconnected from the page the user sees.

In practice, DOM selection is the starting point of almost every interactive feature. You select the button before attaching a click handler. You select the input before reading its value. You select the list container before appending items to it. Getting comfortable with querySelector and querySelectorAll is one of the most immediately useful skills you can build as a frontend developer.

Explanation

The DOM (Document Object Model) is a tree-like representation of the HTML document that the browser creates when it parses the HTML. JavaScript interacts with the DOM through the document object. The DOM is not the HTML file itself — it is a live object in memory that reflects the current state of the page, including any changes JavaScript has made.

document.querySelector(selector) finds and returns the first element in the document that matches the given CSS selector. If no match is found, it returns null. The selector works exactly like CSS — you can use element names ("p"), IDs ("#nav"), classes (".card"), attribute selectors, and combinators. This is the most commonly used DOM selection method.

document.querySelectorAll(selector) returns a NodeList containing all elements that match the selector. A NodeList is array-like — you can iterate over it with forEach or for...of, access items by index, and check its .length. However, it is not a true array, so methods like .map() and .filter() are not available on it directly.

Once you have a reference to an element, you can read its properties: .textContent gives you the text inside the element, .innerHTML gives you the HTML content, .value gives you the current value of an input or select, and .className gives you the class attribute as a string.

You can also scope your selection to a specific part of the document by calling querySelector on an element instead of document. This is called contextual selection and prevents unintended matches elsewhere on the page.

Code Example

This example selects multiple elements from a page and reads their content:

// Select by ID
const heading = document.querySelector("#page-title");

// Select by class (first match)
const firstCard = document.querySelector(".product-card");

// Select all matching elements
const allCards = document.querySelectorAll(".product-card");

// Read element content
console.log(heading.textContent); // the heading text

// Iterate over all matches
allCards.forEach((card, index) => {
  console.log(`Card ${index + 1}:`, card.querySelector("h3").textContent);
});

// Contextual selection: find a button inside a specific card
const buyButton = firstCard.querySelector(".buy-btn");
console.log("Button found:", buyButton !== null);

// Reading an input value
const searchInput = document.querySelector("#search");
console.log("Search value:", searchInput?.value ?? "input not found");

Code Explanation

document.querySelector("#page-title") uses an ID selector to find the unique heading element on the page. ID selectors are fast and unambiguous when you know the element exists only once.

document.querySelectorAll(".product-card") selects every element with the class product-card. The result is a NodeList, not an array. Using .forEach() on the NodeList iterates over each card. Inside the callback, card.querySelector("h3") performs a contextual selection — finding the h3 only within that specific card, not anywhere else on the page.

firstCard.querySelector(".buy-btn") is contextual selection in action. Even though there may be many .buy-btn elements on the page, this call only searches within firstCard. This is a best practice when you have repeated UI components like cards.

The last line uses optional chaining (?.) to safely access searchInput.value in case the element was not found and is null. The nullish coalescing operator (??) provides a fallback string if the value is null or undefined.

Pattern Highlights

Positive Pattern: Use contextual querySelector (called on an element rather than document) when selecting inside repeated UI components — it prevents unintended matches and makes your selections more precise.

⚠️ Neutral Note: querySelectorAll returns a NodeList, not an Array. If you need array methods like .map() or .filter(), convert it first: Array.from(document.querySelectorAll(".card")).

Negative Pattern: Calling methods on the result of querySelector without first checking if it returned null will throw a TypeError and crash your script. Always guard against null when the element might not be on the page.

Common Mistakes

Mistake 1: Calling a method on a null querySelector result

If querySelector finds no matching element, it returns null. Calling any property or method on null throws an error.

const modal = document.querySelector("#confirm-modal");
modal.style.display = "block"; // TypeError if #confirm-modal does not exist

Guard with an if check or optional chaining: modal?.style safely returns undefined instead of crashing.

Mistake 2: Using getElementById and expecting the same format as querySelector

Some tutorials mix getElementById("title") with querySelector("#title"). They both work, but getElementById does not use the # prefix — which causes silent failures when mixed up.

const el1 = document.getElementById("#page-title"); // null — wrong! No # needed
const el2 = document.getElementById("page-title");  // correct
const el3 = document.querySelector("#page-title");  // also correct

Stick to querySelector with CSS selector syntax for consistency and flexibility.

Mistake 3: Treating a NodeList like an Array

querySelectorAll returns a NodeList, not an Array. Array methods like .map() and .filter() do not exist on it.

const cards = document.querySelectorAll(".card");
const titles = cards.map(card => card.textContent); // TypeError: cards.map is not a function

Convert to an array first: const titles = Array.from(cards).map(card => card.textContent) or use [...cards].map(...).

Quick Recap

  • The DOM is the browser's live object representation of the HTML document — JavaScript interacts with it through document
  • querySelector(selector) returns the first matching element or null; querySelectorAll(selector) returns a NodeList of all matches
  • Read element data through properties like .textContent, .value, and .className after you have a reference to the element
  • Always guard against null when an element might not be on the page, and convert NodeLists to arrays when you need array methods

Ready to test your understanding? Take the quiz to reinforce what you learned.

© 2026 Ant Skillsv.26.07.07-23:01