Loading...

JavaScript Selecting Elements

A bakery shelf keeps one heading and three slips. The heading says Rye. The slips say Morning, Noon, and Evening, and the last slip also has the class sold. The shelf is a main with id shelf: an h2 with id loaf, then three p elements with class tag. Selecting asks the live document for those nodes by a name you choose. document.getElementById("loaf") returns the heading, and its text is Rye. document.getElementsByClassName("tag") returns a live list of length 3. document.querySelector(".tag") returns Morning, the first slip. document.querySelectorAll(".tag") returns a static list, and index 1 is Noon. A name the shelf never uses is null from querySelector, and a list of length 0 from querySelectorAll.

Five calls, not a walk down the tree

The DOM page named a heading's parent and counted element children against every node. This page does not read parentElement, nextElementSibling, or children, and it does not call Math. The tree is already there. Here you choose which call returns one node, which call returns a list, and which result means nothing matched.

What each call hands back

Count these on the shelf that holds only the heading and the three slips. The readout file further down adds another paragraph, so a count of p elements changes there.

📊 What each call hands back

Call What comes back On this shelf Easy to mix up
getElementById("loaf") one element, or null heading text Rye a list you must index
getElementsByClassName("tag") live HTMLCollection length 3 writing the dot, as in .tag
getElementsByTagName("p") live HTMLCollection length 3 the heading, which is an h2
querySelector(".tag") first match, or null the slip Morning every slip
querySelectorAll(".tag") static NodeList length 3; index 1 is Noon a live collection
querySelectorAll(".oven") empty NodeList length 0 null

The class method and a CSS selector do not spell the dot the same way. getElementsByClassName("tag") wants the bare name tag. getElementsByClassName(".tag") looks for a class that is literally .tag and returns length 0. querySelector wants CSS, so the dot stays on .tag. querySelector("tag") looks for an element whose tag name is tag, finds none, and returns null. Index 0 is the first slip. Index 1 is Noon. The list from getElementsByClassName is live: a later page that adds another tag slip makes that length grow. querySelectorAll keeps the length it had when you called it. Evening carries two classes, tag and sold, so it belongs to the length-3 list and it is also the one match for sold.

One id, one element

An id is meant to be unique on the page. getElementById returns that one element, or null when the id is absent. It does not return a list, and it only exists on document, not on an element.

One id

javascript
let loaf = document.getElementById("loaf");
console.log(loaf.textContent);
console.log(loaf === null);

The first log is Rye. The second is false, because the heading is on the shelf. If two elements shared the id loaf, this call would still return only the first one in the document.

Class name and tag name

Both calls return a live HTMLCollection. Read length before you pick one slip. Use the four-element shelf, not the readout file.

Class and tag

javascript
console.log(document.getElementsByClassName("tag").length);
console.log(document.getElementsByTagName("p").length);
console.log(document.getElementsByClassName("sold").length);

The logs are 3, 3, and 1. Three slips share tag. All three are p elements, and the heading is an h2, so the tag count matches the class count on this shelf. Only Evening has sold. getElementsByClassName(".tag") is a different call: the dot is not part of the class name, so that length is 0.

The first CSS match

querySelector returns one element, the first in document order, or null.

First match

javascript
console.log(document.querySelector(".tag").textContent);
console.log(document.querySelector("#shelf .sold").textContent);

The first log is Morning. The second is Evening. #shelf .sold means an element with class sold inside the shelf, not the shelf itself. document.getElementById("shelf").querySelector(".sold") starts at the shelf and lands on that same Evening slip.

Every match, then an index

querySelectorAll returns every match in a static NodeList. The list can be empty. It is not null.

All matches

javascript
let tags = document.querySelectorAll(".tag");
console.log(tags.length);
console.log(tags[1].textContent);

The length is 3. Index 1 is Noon. Index 0 is Morning, and index 2 is Evening. This list does not grow when a later script adds a slip. The HTMLCollection from getElementsByClassName("tag") would. One is a snapshot. The other stays live.

A name the shelf never wrote

Ask for an id and a class that are not in the file.

Nothing matched

javascript
console.log(document.getElementById("oven"));
console.log(document.querySelector(".oven"));
console.log(document.querySelectorAll(".oven").length);

The first log is null. The second is null. The third is 0. querySelectorAll still returns a list when nothing matches, so you read length 0, and the list itself is not null. getElementById has no list to hand back, so a miss is null, not 0.

A line on the page

One paragraph reports how many tag slips there are, the first slip, and the sold slip. The script sits at the end of the body, after those elements exist. The line has no class tag, so it is not one of the three.

Page demo — three slips

html
<!DOCTYPE html>
<html>
<head>
<title>selecting — page</title>
</head>
<body>
<main id="shelf">
<h2 id="loaf">Rye</h2>
<p class="tag">Morning</p>
<p class="tag">Noon</p>
<p class="tag sold">Evening</p>
<p id="line">…</p>
</main>
<script>
let tags = document.querySelectorAll(".tag");
let line = document.getElementById("line");
line.textContent = tags.length + " / " + tags[0].textContent + " / " + document.querySelector(".sold").textContent;
</script>
</body>
</html>

The paragraph reads 3 / Morning / Evening. On this file, getElementsByTagName("p") counts 4, because the line is another p. querySelectorAll(".tag") still counts 3. The next page changes the HTML of elements these calls already found.

Check the shelf

  1. Log the heading text and expect Rye, then the null check and expect false
  2. Log the class length, the p length, and the sold length: 3, 3, and 1
  3. Log the first .tag text and expect Morning, then #shelf .sold and expect Evening
  4. Log the querySelectorAll length and expect 3, then index 1 and expect Noon
  5. Log the missing id and the missing querySelector and expect null both times
  6. Log querySelectorAll(".oven").length and expect 0
  7. Read the paragraph and expect 3 / Morning / Evening

Wrong call

The page still opens. The call you wrote asked for the wrong thing.

  • Passing .tag to getElementsByClassName, where the dot belongs only in a CSS selector
  • Passing tag without a dot to querySelector and expecting the slips
  • Reading index 1 as the first slip
  • Treating a querySelectorAll miss as null instead of length 0
  • Treating a getElementById miss as a list of length 0
  • Counting getElementsByTagName("p") on the readout file and expecting 3
  • Calling .map on the HTMLCollection; it is not an array
  • Walking parentElement on this page; that was the tree page

Leave with this

  • getElementById returns one element, or null
  • getElementsByClassName and getElementsByTagName return a live HTMLCollection
  • The class method wants tag, not .tag
  • querySelector returns the first CSS match, or null
  • querySelectorAll returns a static NodeList; a miss has length 0
  • Index 0 is Morning and index 1 is Noon
  • The next page changes HTML on elements you have already selected

🧠 Test Your Knowledge

Ready to Start

Test Your Knowledge

Challenge yourself with this interactive quiz and see how well you understand the topic

❓
8
Questions
🎯
70%
To Pass
♾️
∞
Time
🔄
∞
Attempts

📝 Instructions

  • Read each question carefully
  • Select the best answer for each question
  • You can retake the quiz as many times as you want
  • Your progress will be shown at the top