Skip to main content

DOM selectors in JavaScript

DOM selectors are the ways to find elements on a page with JavaScript. Without them you cannot interact with HTML at all: you first have to get the element in order to do something with it (change its text or style, remove it, attach an event handler and so on).

Theory

TL;DR

  • A selector is a way to say which element in the DOM you want: by id, tag, class, attribute or a full CSS selector.
  • getElementById returns a single element, while getElementsByClassName and getElementsByTagName return an HTMLCollection.
  • querySelector returns the first match for a CSS selector, querySelectorAll returns all matches as a NodeList.
  • The getElementsBy... collections are live: a new element in the DOM shows up in them automatically. The NodeList from querySelectorAll is static.
  • You can narrow the search down: a found element also has querySelector and querySelectorAll.

Quick example

javascript
// HTML: <ul class="menu"><li>Home</li><li class="active">Profile</li></ul> const menu = document.querySelector('.menu'); // the first match const items = menu.querySelectorAll('li'); // search inside menu const active = document.querySelector('.menu li.active'); console.log(items.length); // 2 console.log(active.textContent); // Profile

The main element lookup methods

MethodWhat it returnsWhat it takesExample
getElementById(id)a single elementid (without #)document.getElementById("header")
getElementsByClassName(class)HTMLCollection (array like)class name (without .)document.getElementsByClassName("menu")
getElementsByTagName(tag)HTMLCollectiontag namedocument.getElementsByTagName("p")
querySelector(selector)the first elementCSS selectordocument.querySelector(".menu li")
querySelectorAll(selector)NodeList (every match)CSS selectordocument.querySelectorAll("div > p")

Each method in detail

document.getElementById() finds one element by its id attribute.

html
<h1 id="title">Hello!</h1>
javascript
const el = document.getElementById('title'); console.log(el.textContent); // Hello!

Pro: very fast. Cons: works by id only and returns a single element.

document.getElementsByClassName() looks for all elements with the given class.

html
<p class="text">First</p> <p class="text">Second</p>
javascript
const els = document.getElementsByClassName('text'); console.log(els.length); // 2

Pro: fast and supported everywhere. Con: it returns an HTMLCollection, which you have to convert to an array (Array.from) before using forEach.

document.getElementsByTagName() finds every element by tag.

javascript
const paragraphs = document.getElementsByTagName('p');

It returns a live collection: if a new <p> appears in the DOM, it is added to the collection automatically.

document.querySelector() uses CSS selectors and returns the first matching element.

javascript
const item = document.querySelector('.menu li.active');

Pro: universal, it supports everything CSS selectors can express. Con: it returns only one element.

document.querySelectorAll() returns every element that matches the CSS selector.

javascript
const items = document.querySelectorAll('.menu li'); items.forEach((li) => console.log(li.textContent));

Pro: it returns a NodeList, which already has forEach(). Con: the collection does not update itself, unlike getElementsBy....

Types of selectors (as in CSS)

SelectorExampleDescription
By tag"div"every <div>
By class".item"every element with the class item
By id"#header"the element with id="header"
By attribute"[type='text']"elements with type="text"
Descendants"ul li"every <li> inside a <ul>
Direct children"ul > li"only the ones one level deep
Combined"div.item.active"<div class="item active">
Pseudo classes"li:first-child", "input:checked"specific states

When to use what

GoalBetter to use
Get an element by id quicklygetElementById()
Find by class or taggetElementsByClassName() / getElementsByTagName()
A complex lookup (as in CSS)querySelector() / querySelectorAll()
A collection that updates itselfgetElementsBy...
A one off static listquerySelectorAll()

An example of a combined lookup. HTML:

html
<div class="card"> <h2 class="title">Title</h2> <p class="desc">Description</p> </div>

JavaScript:

javascript
const card = document.querySelector('.card'); const title = card.querySelector('.title'); const desc = card.querySelector('.desc'); console.log(title.textContent); // "Title"

A practical tip: reach for querySelector / querySelectorAll by default, they are the universal and modern way and work with any CSS selector. getElementById and getElementsByClassName are useful where you need maximum performance (legacy code or thousands of elements).

Working with the elements you found

Once the element is found you can do almost anything with it:

javascript
const el = document.querySelector('#myDiv'); el.textContent = 'New text'; // change the content el.style.color = 'red'; // change the style el.classList.add('active'); // add a class el.addEventListener('click', onClick); // attach a handler

A short recap of the return values:

MethodReturnsNotes
getElementById(id)a single elementfast, unique id
getElementsByClassName()HTMLCollectiona live collection
getElementsByTagName()HTMLCollectionlookup by tag
querySelector()the first elementCSS selector
querySelectorAll()NodeListevery matching element

Common mistakes

  • Passing # or . where they are not expected. getElementById('#title') returns null: this method takes a bare id. querySelector('#title'), on the other hand, requires the #.
  • Calling forEach on an HTMLCollection. getElementsByClassName / getElementsByTagName have no forEach; use Array.from(els) first, or switch to querySelectorAll.
  • Not checking the result for null. querySelector and getElementById return null when nothing is found, and touching .textContent then throws immediately.
  • Forgetting that collections are live. A loop over an HTMLCollection that removes elements inside the body skips some of them, because the collection changes as you go.
  • Looking for elements before the HTML is parsed. A script in <head> without defer runs before the markup exists, so the selector finds nothing.
  • Always searching from document. If you already hold a container, call card.querySelector('.title'): it is both faster and more precise.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.