Skip to content

Selecting Elements

Before you can read or change anything on the page, you need to get a reference to the element you want to work with. Selecting an element means asking the DOM: give me this specific node.

querySelector accepts any CSS selector and returns the first matching element. If nothing matches, it returns null.

const nav = document.querySelector('nav');
const heading = document.querySelector('h1');
const firstCard = document.querySelector('.tour-card');
const submitBtn = document.querySelector('#contact-form button[type="submit"]');

This is the most flexible selector — it accepts the same syntax as CSS, so any selector you already know works here. Use it as your default for selecting a single element.

querySelectorAll accepts any CSS selector and returns a NodeList of all matching elements:

const allCards = document.querySelectorAll('.tour-card');
const allImages = document.querySelectorAll('img');
const navLinks = document.querySelectorAll('nav a');
console.log(allCards.length); // number of tour cards on the page

A NodeList is iterable — you can use for...of to loop over the results:

for (const card of allCards) {
console.log(card.textContent);
}

Use querySelectorAll as your default for selecting multiple elements.

getElementById selects a single element by its id attribute. It is fast and widely used in older code:

const contactForm = document.getElementById('contact-form');

Note: no # prefix — just the id string, unlike querySelector('#contact-form').

querySelector('#contact-form') and getElementById('contact-form') return the same element. For new code, querySelector is preferred because it accepts any selector and is consistent with querySelectorAll.

getElementsByClassName returns a live HTMLCollection of elements with a given class:

const cards = document.getElementsByClassName('tour-card');

This is an older API. Prefer querySelectorAll('.tour-card') for new code — it returns a static NodeList and accepts any CSS selector.

NodeListHTMLCollection
Returned byquerySelectorAllgetElementsBy*
Updates automatically?No (static snapshot)Yes (live)
Iterable with for...of?YesYes
Has .forEach()?YesNo

The “live” behavior of HTMLCollection means it updates automatically when the DOM changes — which can cause subtle bugs when you are modifying elements during iteration. Stick to querySelectorAll to avoid this.

When querySelector or getElementById does not find a match, it returns null. Calling any method on null throws a TypeError:

const nav = document.querySelector('nav');
nav.classList.add('loaded'); // TypeError if nav is null

For elements you are confident exist, this is not a concern. For elements that might be absent depending on the page, check first:

const nav = document.querySelector('nav');
if (nav) {
nav.classList.add('loaded');
}

In the STO project with defer on your script, all elements are guaranteed to exist when the script runs. Null checks matter most when code runs on multiple pages where an element might not always be present.

On the STO site, open the Console and run each selector:

// Single element selectors
const nav = document.querySelector('nav');
console.log(nav);
const mainHeading = document.querySelector('h1');
console.log(mainHeading);
// Multiple element selector
const tourCards = document.querySelectorAll('.tour-card');
console.log(tourCards.length);
for (const card of tourCards) {
console.log(card.querySelector('h3')?.textContent);
}
// Check for null
const missing = document.querySelector('.does-not-exist');
console.log(missing); // null

Inspect each result in the Elements panel by hovering over the logged element in the console.

  • document.querySelector(selector) returns the first matching element or null. Use it for single elements.
  • document.querySelectorAll(selector) returns a static NodeList of all matches. Use for...of to iterate. Use it for multiple elements.
  • document.getElementById(id) selects by id without a # prefix — querySelector is preferred for new code.
  • getElementsByClassName returns a live HTMLCollection — prefer querySelectorAll to avoid live-update surprises.
  • Always check for null before calling methods on elements that may not exist on every page.