Day 10: The DOM: Select and Change the Page
✅ Today you will learn
- What the DOM is – the page as a tree of objects
- Selecting: querySelector, querySelectorAll, getElementById, closest
- Changing text: textContent vs innerHTML
- classList for styling, style for one-off values
- Attributes, dataset and form values
🏢 In the real world
Everything a user sees changing on a website – a price updating, a heart icon turning red, “Out of stock” appearing, the dark-mode switch – is JavaScript changing the DOM. Today you learn the 10 DOM operations that cover 90% of real front-end work.
The DOM is a tree
HTML is the text file you wrote. The DOM is the live tree in memory. JavaScript changes the DOM – View Source still shows the original HTML, while DevTools → Elements shows the current DOM.
Selecting elements
<h2 id="title">Today’s sweets</h2>
<ul class="menu">
<li class="item">Kaju Katli</li>
<li class="item special">Ghewar</li>
<li class="item">Rasmalai</li>
</ul>
<pre id="log"></pre>
<script>
const title = document.querySelector('#title'); // first match (CSS selector)
const items = document.querySelectorAll('.menu .item'); // ALL matches (NodeList)
const special = document.querySelector('.item.special');
const byId = document.getElementById('title'); // older, very fast
const lines = [];
lines.push('title text: ' + title.textContent);
lines.push('items found: ' + items.length);
items.forEach((li, i) => lines.push(` ${i}: ${li.textContent}`));
lines.push('special: ' + special.textContent);
lines.push('same element? ' + (title === byId));
lines.push('parent of special: ' + special.closest('ul').className);
document.querySelector('#log').textContent = lines.join('\n');
</script>| Method | Returns | Tip |
|---|---|---|
| querySelector(css) ⭐ | First element or null | Any CSS selector: "#id", ".class", "form input[name=phone]" |
| querySelectorAll(css) | NodeList (can forEach) | Use [...list] to get array methods like map |
| getElementById(id) | Element or null | No # in the argument |
| el.closest(css) | Nearest ancestor (or itself) | Find the card that contains a clicked button |
| el.querySelector(css) | Search inside el only | card.querySelector(".price") |
Changing text and HTML
<p id="a"></p>
<p id="b"></p>
<p id="c"></p>
<script>
const userInput = '<img src=x onerror="alert(1)"> Aarti';
document.querySelector('#a').textContent = 'Price: ₹900/kg'; // ✅ plain text
document.querySelector('#b').innerHTML = 'Price: <b>₹900</b>/kg'; // ✅ your own trusted HTML
document.querySelector('#c').textContent = 'Hello ' + userInput; // ✅ user text shown safely as text
// document.querySelector('#c').innerHTML = 'Hello ' + userInput; // ❌ would run the attacker’s code
</script>Never put text typed by users (names, reviews, search terms) into innerHTML. An attacker can inject a script that steals logins. Use textContent, or build elements (Day 12). Day 26 covers security in depth.
classList and style
<style>
.card { width: 240px; border: 2px solid #e5e7eb; border-radius: 14px; padding: 14px; transition: .2s; }
.card.selected { border-color: #ca8a04; background: #fefce8; }
.card.sold-out { opacity: .5; }
.badge { display: inline-block; background: #16a34a; color: #fff; font-size: 12px; padding: 2px 8px; border-radius: 99px; }
button { margin: 4px 4px 0 0; }
</style>
<article class="card" id="card">
<h3>Kaju Katli <span class="badge">Veg</span></h3>
<p>₹<span id="price">900</span>/kg</p>
<button id="select">Select</button><button id="sold">Toggle sold out</button><button id="sale">Festival price</button>
</article>
<script>
const card = document.querySelector('#card');
document.querySelector('#select').addEventListener('click', () => {
card.classList.toggle('selected'); // add if missing, remove if present
});
document.querySelector('#sold').addEventListener('click', () => {
const soldOut = card.classList.toggle('sold-out');
card.querySelector('.badge').textContent = soldOut ? 'Sold out' : 'Veg';
card.querySelector('.badge').style.background = soldOut ? '#dc2626' : ''; // '' resets to CSS
});
document.querySelector('#sale').addEventListener('click', () => {
document.querySelector('#price').textContent = 810;
card.style.setProperty('border-style', 'dashed');
});
</script>| classList method | Does |
|---|---|
| add("x") | Add a class |
| remove("x") | Remove a class |
| toggle("x") | Add/remove; returns true if now present |
| toggle("x", condition) | Force on/off from a boolean |
| contains("x") | true/false |
| replace("a", "b") | Swap classes |
Attributes, data-* and form values
<img id="photo" src="images/kaju-katli-400.webp" alt="Kaju katli" width="160">
<div>
<button class="pick" data-img="images/ghewar-400.webp" data-name="Ghewar" data-price="650">Ghewar</button>
<button class="pick" data-img="images/rasmalai-400.webp" data-name="Rasmalai" data-price="700">Rasmalai</button>
<button class="pick" data-img="images/ladoo-400.webp" data-name="Besan Ladoo" data-price="500">Ladoo</button>
</div>
<label>Qty (kg) <input id="qty" type="number" value="1" min="0.25" step="0.25"></label>
<label><input id="gift" type="checkbox"> Gift wrap (+₹50)</label>
<p id="total"></p>
<script>
let price = 900;
function update() {
const qty = Number(document.querySelector('#qty').value); // .value is always a string
const gift = document.querySelector('#gift').checked; // .checked for checkboxes
document.querySelector('#total').textContent = `Total ₹${price * qty + (gift ? 50 : 0)}`;
}
document.querySelectorAll('.pick').forEach(btn => btn.addEventListener('click', () => {
const photo = document.querySelector('#photo');
photo.src = btn.dataset.img; // data-img → dataset.img
photo.alt = btn.dataset.name;
price = Number(btn.dataset.price);
document.querySelectorAll('.pick').forEach(b => b.setAttribute('aria-pressed', b === btn));
update();
}));
document.querySelector('#qty').addEventListener('input', update);
document.querySelector('#gift').addEventListener('change', update);
update();
</script>| Read / write | Example |
|---|---|
| Common properties | img.src, a.href, input.value, input.checked, el.id, btn.disabled |
| Any attribute | el.getAttribute("aria-label"), el.setAttribute("aria-pressed", "true"), el.removeAttribute("hidden") |
| data-* attributes | data-price="650" → el.dataset.price ("650", a string) |
| hidden | el.hidden = true – hides without CSS |
Real task: a dark-mode switch
<style>
:root { --bg: #fff; --text: #111827; --card: #f1f5f9; }
:root.dark { --bg: #0b1120; --text: #e5e7eb; --card: #1e293b; }
body { background: var(--bg); color: var(--text); transition: background .3s, color .3s; }
.panel { background: var(--card); padding: 12px 16px; border-radius: 12px; margin-top: 10px; }
</style>
<button id="theme" aria-pressed="false">🌙 Dark mode</button>
<div class="panel"><h3>Order #1042</h3><p>2 kg Kaju Katli · ₹1,800</p></div>
<script>
const btn = document.querySelector('#theme');
btn.addEventListener('click', () => {
const dark = document.documentElement.classList.toggle('dark');
btn.setAttribute('aria-pressed', dark);
btn.textContent = dark ? '☀️ Light mode' : '🌙 Dark mode';
});
</script>🏅 Job skills unlocked today
Practice tasks
🏋️ Practice – do it yourself
Show solution
Quick quiz
🧠 Quick Quiz – test yourself
Interview corner
💼 Interview Corner
textContent vs innerText vs innerHTML?
textContent gets/sets raw text of all nodes (fast, ignores CSS); innerText returns rendered visible text (triggers layout); innerHTML parses and returns HTML – powerful but an XSS risk with untrusted data.
NodeList vs HTMLCollection?
querySelectorAll returns a static NodeList (supports forEach); getElementsByClassName returns a live HTMLCollection that updates as the DOM changes and lacks forEach. Convert either with Array.from or spread.
Summary
- The DOM is the live tree of your page.
- querySelector / querySelectorAll / closest select elements.
- textContent for text; innerHTML only for trusted HTML.
- classList for design states, dataset and attributes for data.
Day 11: events – clicks, typing, keyboard shortcuts, the event object and event delegation.
