Select existing elements deliberately
DOM selection returns references to nodes in the current document. getElementById takes an ID string without #. querySelector takes a CSS selector and returns the first matching element or null. querySelectorAll returns a static NodeList of matching elements.
Place the script after the markup or use a correctly loaded deferred/module script. Searching before parsing the elements normally returns null or an empty list, not a reference that automatically fills in later.
Scope the search
Selecting from section narrows the search to its descendants. This avoids accidentally matching a similar button in another widget. Keep IDs unique. If using untrusted text in a selector, validate the intended lookup or escape the selector component appropriately rather than concatenating arbitrary syntax.
Static versus live collections
A NodeList from querySelectorAll is a snapshot of matches at the time of selection. Adding another .lesson later does not increase that existing list's length. Other DOM collection APIs may be live; know the contract before mutating during iteration.
Handle missing elements
const status = document.querySelector('#status');
if (status) status.textContent = 'Ready';A guard is appropriate for an optional widget. For a required element, a clear initialization error may be better than silently doing nothing. A null access error should prompt checking the selector and script timing.
Practice
- Select all lesson buttons only inside one section.
- Add another button and compare a fresh query with the old NodeList.
- Use a misspelled ID and inspect the null result.
- Explain why getElementById('#lessons') is wrong for an ID whose value is lessons.