17. DOM Selection and Inspection
The browser parses HTML into a DOM (Document Object Model) tree. JavaScript reads and updates this tree to change what the user sees.
What you'll learn
- 1Understand the DOM tree.
- 2Select elements with `querySelector`, `querySelectorAll`, `getElementById`.
- 3Read attributes with `getAttribute` and `dataset`.
- 4Know the difference between `textContent` and `innerHTML` (especially XSS).
- 5Navigate parent/child/sibling nodes.
Overview
The browser parses HTML into a DOM (Document Object Model) tree. JavaScript reads and updates this tree to change what the user sees.
Core Concepts
1. What is the DOM?
The browser parses HTML into a DOM (Document Object Model) tree. JavaScript reads and updates this tree to change what the user sees.
2. Selecting elements
const title = document.querySelector("#title"); // first match
const items = document.querySelectorAll(".item"); // NodeList
const byId = document.getElementById("title");`querySelector` accepts a regular CSS selector. `querySelectorAll` returns a static NodeList that supports forEach.
3. Reading attributes
const link = document.querySelector("a");
link.getAttribute("href");
link.href; // DOM property
link.dataset.userId; // the data-user-id attribute4. textContent vs innerHTML
`textContent` reads/writes plain text, so it's safe. `innerHTML` parses an HTML string, so dropping user input into it exposes you to XSS. Always use `textContent` for external input, or sanitize first.
5. Tree navigation
el.parentElement;
el.children;
el.firstElementChild;
el.nextElementSibling;Examples
| File | What it covers |
|---|---|
| index.html | HTML page used by the examples |
| 01_query_selector.js | querySelector/All in use |
| 02_attributes.js | getAttribute / dataset |
| 03_text_html.js | textContent vs innerHTML |
| 04_nested.js | Parent/child/sibling traversal |
src/01_query_selector.js
/**
* Compare querySelector / querySelectorAll / getElementById.
*/
const title = document.querySelector("#title");
console.log("title:", title.textContent);
const items = document.querySelectorAll(".item");
console.log("item count:", items.length);
items.forEach((el) => console.log("-", el.textContent));
const byId = document.getElementById("msg");
console.log("msg:", byId.textContent);
src/02_attributes.js
/**
* Read attributes and dataset.
*/
const link = document.querySelector("#link");
console.log("href(attr):", link.getAttribute("href"));
console.log("href(prop):", link.href);
document.querySelectorAll(".item").forEach((el) => {
console.log("id =", el.dataset.id, ", text =", el.textContent);
});
src/03_text_html.js
/**
* Differences between textContent and innerHTML.
* Putting user input directly into innerHTML is an XSS risk.
*/
const msg = document.querySelector("#msg");
console.log("textContent:", msg.textContent);
console.log("innerHTML:", msg.innerHTML);
msg.textContent = "<b>safe</b>";
console.log("after textContent:", msg.textContent);
src/04_nested.js
/**
* Parent / child / sibling traversal.
*/
const list = document.querySelector("#list");
console.log("children:", list.children.length);
console.log("first child:", list.firstElementChild.textContent);
console.log("second child:", list.firstElementChild.nextElementSibling.textContent);
console.log("parent tag:", list.parentElement.tagName);
src/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>DOM selection and inspection</title>
</head>
<body>
<h1 id="title">DOM example</h1>
<ul id="list">
<li class="item" data-id="1">apple</li>
<li class="item" data-id="2">banana</li>
<li class="item" data-id="3">cherry</li>
</ul>
<a href="https://example.com" id="link">example link</a>
<p id="msg">Hello</p>
<script src="01_query_selector.js"></script>
<script src="02_attributes.js"></script>
<script src="03_text_html.js"></script>
<script src="04_nested.js"></script>
</body>
</html>
Common Mistakes
- Loading a script in `<head>` and trying to touch the DOM before it exists.
- Treating `querySelectorAll` results as arrays β they're NodeLists, you may need `Array.from`.
- Putting user input directly into `innerHTML`.
- Ignoring the subtle difference between `getAttribute("class")` and `el.className`.
- Relying on a behavior when multiple elements share the same id.
FAQ
Q1. getElementById or querySelector("#id")?
The performance difference is negligible. Use querySelector consistently if you prefer.
Q2. NodeList vs HTMLCollection?
NodeList is static (or live); HTMLCollection is always live and has no forEach.
Q3. dataset naming rules?
data-user-id maps to dataset.userId (kebab-case β camelCase).
Practice
Select every element with a given class in your page and print the list to the console.
homework/README.md
Select every `.fruit` item in the HTML and print:
- The text of each item
- The value of its data-color attribute
See `answer/homework_01.html` and `answer/homework_01.js` for the solution.
homework/answer/homework_01.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Homework 17-1</title>
</head>
<body>
<ul id="fruits">
<li class="fruit" data-color="red">apple</li>
<li class="fruit" data-color="yellow">banana</li>
<li class="fruit" data-color="purple">grape</li>
</ul>
<script src="homework_01.js"></script>
</body>
</html>
homework/answer/homework_01.js
/**
* Print the text and color of every .fruit element.
*/
const fruits = document.querySelectorAll(".fruit");
fruits.forEach((el) => {
console.log(`${el.textContent} -> ${el.dataset.color}`);
});
Next Lecture
[18_DOM_Manipulation](../18_DOM_μ‘°μ/) β Creating, inserting, and removing elements.
All lecture materials and example code are openly available on GitHub.
View on GitHub β