Unit 2 home
FSWD · MERN CLASS 16 / 48 60-MIN SESSION THE DOM
UNIT 2 · JAVASCRIPT (ES6) PART G · CLASS 4 OF 8 UI23PC510CS · THEORY

Class 16 — JavaScript finally touches the page

So far your code only spoke to the console. Today it reaches into the page — and changes what the visitor actually sees.

Every console.log you have written so far talks to a hidden developer panel — the visitor never sees it. But a real website changes on screen: a price updates, a heading turns green, an error message appears. To do that, JavaScript needs a way to grab a piece of the page and edit it. That way is the DOM — the Document Object Model — the browser's live, editable model of your HTML. Today you'll learn to select an element, read and change its text, and restyle it from JavaScript — with every change happening for real, in front of you.

Walk out of this room able to…

Explain the DOM in one sentence — the browser's live tree of your HTML that JavaScript can read and change
Select elements — getElementById, querySelector and querySelectorAll
Change an element's .textContent and its .style live from JS
Read the two most common DOM errors — Cannot set properties of null and undefined — and fix them
TODAY, POINT BY POINT
01The DOM in plain English — your HTML drawn as a live treeIDEA
02The document object — the entry point to the whole pageIDEA
03getElementById — grabbing one element by its idBUILD
04querySelector / querySelectorAll — CSS-style selection, at scaleBUILD
05Activity — watch the DOM change live in DevToolsTRY IT
06.textContent — reading and changing an element's wordsBUILD
07.style.property — restyling an element from JavaScriptBUILD
08Activity — the broken-site debugging drill (null from a typo'd id)DEBUG
09The window object — window.innerWidth, and a self-study cardREAD
10Activity — console-error diagnosis (undefined before the DOM loads)DEBUG
11Take-home kit + folder summary, and the bridge to Lab 4WRAP
NOTHING TO DOWNLOAD TODAY — ZERO ASSETS

Every DOM demo runs live, right on this slide. To practise on your own machine, make a fresh throwaway folder fswd-practice\Class-16\, open it in VS Code, and create a scratch pair — one index.html and one script.js beside it — build tiny pages from scratch, run them, throw them away. Nothing here is committed: fswd-practice\ is scratch paper, never a git repo. The real poshtik-campus\ project — and its commits — live only in the lab classes. Never here.

To see the DOM on your own machine: open your .html file in the browser, then press F12 (or right-click → Inspect). The Elements tab shows the live DOM tree; the Console tab is where every console.log and every red error appears. You'll live in both tabs today.

The one idea everything else stands on

The DOM is your HTML, drawn as a living tree the browser keeps in memory.

When the browser reads your HTML file, it doesn't just paint it once and forget it. It builds a tree in memory — one node for every tag — and keeps that tree alive the whole time the page is open. That tree is the DOM. Your HTML file is the blueprint (written once, on disk); the DOM is the live building (standing in memory, editable). JavaScript never edits your .html file — it edits the live tree, and the screen instantly redraws to match.

Your HTML → the DOM tree the browser builds from it document <html> <head> <body> <title> <h1> <p> <p> "Poshtik Campus" text node Each tag = one node. Text inside a tag is its own text node. JS edits a node → screen redraws.

Say it in one line: "The DOM is the browser's live, in-memory tree of my HTML — one node per tag — and JavaScript changes the page by changing that tree." The words "live" and "tree" are the two the examiner is listening for. The file on disk never changes; the tree in memory does.

EXTRA DEPTH · WHY "OBJECT MODEL"?

You just spent Class 15 on objects — bundles of labelled values you read with a dot. The DOM is exactly that idea applied to the page: every node is an object with properties (.textContent, .style, .id) you read and set with a dot, just like dish.price. That is literally why it's called the Document Object Model: your whole page is one big tree of objects. Everything you learned yesterday transfers directly today.

Before any JavaScript: build the page it will reach into — from scratch

A DOM lesson with no page is a lesson about reaching into thin air. So we build the page first, by hand, one line per press — nothing downloaded, nothing pre-made. This is the exact tree you just saw drawn above, now written as a real file. Read the three ids and the one repeated class carefullysite-name, tagline, price, and class="dish". Every single line of JavaScript for the rest of today reaches for one of those four names. Nothing else in this class is new HTML; this one file carries the whole hour.

campus.html · complete file, from scratchBUILDS ONE PRESS AT A TIME
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <title>Poshtik Campus</title> <!-- document.title reads THIS -->
6 <link rel="stylesheet" href="style.css">
7</head>
8<body>
9 <h1 id="site-name">Poshtik Campus</h1>
10 <p id="tagline">Healthy food, made on campus.</p>
11 <p>Today's special: <span id="price">Ragi Idli — ₹30</span></p>
12
13 <p class="dish">Ragi Idli</p>
14 <p class="dish">Millet Pongal</p>
15 <p class="dish">Sprout Salad</p>
16
17 <script src="script.js"></script> <!-- LAST line in body. Part 11 explains why -->
18</body>
19</html>
·19 lines, no CSS framework, no library. THREE ids + ONE repeated class — that is the entire surface today's JavaScript grabs.
file:///C:/Users/student/Desktop/fswd-practice/Class-16/campus.html
Poshtik Campus

Healthy food, made on campus.

Today's special: Ragi Idli — ₹30

Ragi Idli

Millet Pongal

Sprout Salad

↑ PLAIN HTML ONLY — style.css IS STILL AN EMPTY FILE, SO NO COLOURS AND NO RULES YET

✓ Page built — and deliberately unstyled: style.css is linked on line 6 but still empty, so you are seeing raw browser defaults. The next panel fills that sheet in, one rule at a time. script.js is wired last in <body> (Part 11 explains why).

Why the three ids are not decoration. An id is a handle — the name JavaScript uses to grab one exact element. Look at line 9: because that <h1> carries id="site-name", Part 4 can write document.getElementById("site-name") and get that heading and no other. No id, no handle, no reach. And the repeated class="dish" on lines 13–15 is the opposite tool: a label shared by many elements, which is exactly what querySelectorAll(".dish") in Part 5 collects in one line.

The stylesheet, also from scratch — because Part 8 is going to fight with it

Line 6 of the page linked a style.css, so let's write it — kept deliberately tiny, only what the demos need to be visible. Pay attention to lines 11–15: they give #price a plain grey, bold look. Remember that grey. In Part 8 JavaScript will set .style.color on that same element and win against this rule — and you'll see exactly why.

style.css · complete file, from scratchBUILDS ONE PRESS AT A TIME
1/* style.css — only what today's demos need to be visible */
2body
3{
4 font-family: Georgia, serif;
5 padding: 20px;
6}
7h1
8{
9 font-size: 26px;
10}
11#price /* Part 8's JavaScript will override this */
12{
13 color: #333333;
14 font-weight: bold;
15}
16.dish
17{
18 border-bottom: 1px solid #cccccc;
19}
·19 lines of CSS, four rules. Selector shapes you already own from Unit 1: tag (body, h1), id (#price), class (.dish).
SAME PAGE AS ABOVE — PLAIN FIRST, THEN EACH RULE LANDS
file:///C:/Users/student/Desktop/fswd-practice/Class-16/campus.html
Poshtik Campus

Healthy food, made on campus.

Today's special: Ragi Idli — ₹30

Ragi Idli

Millet Pongal

Sprout Salad

↑ PLAIN HTML ONLY — style.css IS STILL EMPTY

Watch the same page change four times: the whole page switches to Georgia and gains breathing room, the heading grows, the price turns grey and bold, then each dish gets its hairline rule.

✓ Styled. Now the page has a look that JavaScript can be caught changing — and that grey #price is the exact rule Part 8 defeats.
EXTRA DEPTH · THIS IS ALL THE HTML/CSS TODAY NEEDS

Notice how little markup a serious JavaScript lesson requires: 19 lines of HTML and 19 of CSS, all of it built in front of you from Unit 1 skills you already have. That is deliberate. From here on, every new thing you see is JavaScript — if a demo mentions an element, it is one of the four names on this page (site-name, tagline, price, .dish). Keep this file's picture in your head and no selector today can surprise you.

Your one door into the page

The document object — the single entry point to the whole DOM tree.

You don't reach into the tree by magic. The browser hands you one ready-made object called document — it is the page. From document you can reach anything: its title (document.title), its body (document.body), and — most importantly — the methods that find elements inside it. Everything you do today starts with the word document.

Build the page from scratch, then read and change its own title

A .js file cannot be double-clicked into life — before any DOM code exists there must be a page for it to reach into. So the same fixed order as always: plain HTML first, then CSS one rule per press, then the JavaScript. Watch the right: the page appears with browser-default looks, each CSS rule lands, then the script runs and the tab label changes while the <h1> stays put.

title.html + style rules + script.js · document.title, plain HTML → CSS → JSHTML, THEN CSS, THEN JS — ONE PRESS PER LINE
·<!-- FILE 1 of 3 · title.html — the page whose title we read and set -->
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <title>Poshtik Campus</title> <!-- what document.title reads -->
6</head>
7<body>
8 <h1>Poshtik Campus</h1>
9 <p class="tip">Watch the tab label, not this page.</p>
10</body>
11</html>
·/* HTML complete & PLAIN above. FILE 2 of 3 · the CSS — one rule per press ↓ */
6 <style>
7 body { font-family: Georgia, serif; padding: 18px; }
8 h1 { color: #7C3AED; font-size: 20px; }
9 .tip { color: #64748B; font-size: 13px; }
10 </style>
·<!-- CSS done. Hire the passenger — last line before </body> -->
11 <script src="script.js"></script>
·// ————— FILE 3 of 3 · script.js — the smallest possible DOM edit —————
1// document IS the page — start every DOM job here
2console.log(document.title); // read the tab text
3// setting it changes the browser tab, live
4document.title = "Menu · Poshtik Campus";
5console.log(document.title); // read again — it really changed
·a property you can READ (line 2) and SET (line 4). The <h1> never moved — only the TAB changed, because document.title is the <title> in the head, not the body.
REAL OUTPUT — PLAIN PAGE, THEN CSS, THEN THE TAB CHANGES
Poshtik CampusMenu · Poshtik Campus

Poshtik Campus

Watch the tab label, not this page.

PLAIN HTML — NO CSS AND NO SCRIPT YET
DEVTOOLS · CONSOLE — THE REAL OUTPUT
Poshtik Campusbefore the change
Menu · Poshtik Campusafter the change
Two surfaces, as always — plus a third thing to notice: the tab label changed while the <h1> stayed put. document.title edits the <title> in the head, not the body.

Now run those two lines for real, yourself

The panel above showed the sequence. This device is live: press each button and the fake tab's label really updates, driven by the same one-line code shown on the button.

THIS TAB TITLE IS REAL — WATCH IT CHANGE
Poshtik Campus
CLICK TO RUN EACH LINE FOR REAL
press a button — the code that runs shows here, and the tab above updates
EXTRA DEPTH · document IS GLOBAL — YOU NEVER DECLARE IT

You never write let document = …. The browser creates it for you the moment the page loads and puts it in the global scope, so it's just there, everywhere in your script. Same with window (Part 9). These two are the only "free" objects you get without asking — everything else you build yourself.

The most-used line in front-end code

document.getElementById() — grab exactly one element by its id.

To change an element, you first have to hold it. The oldest and clearest way is by its id — the unique label you give a single element in the HTML. document.getElementById("price") hunts through the tree and hands you back that one element as an object, which you usually store in a variable so you can edit it on the next line. An id is unique, so this always returns one element (or null if nothing matches — remember that for Part 8).

The three lines of HTML this whole Part depends on

Every id JavaScript grabs had to be written in HTML first. So before the code, here is the markup — three plain lines, three ids, nothing else. Step it and watch the plain page appear; then one CSS line gives #price its grey so you can later catch JavaScript overruling it.

campus.html (the three id lines) + style.css · plain HTML first, then CSSHTML BUILDS, THEN CSS — ONE PRESS PER LINE
1<body>
2 <h1 id="site-name">Poshtik Campus</h1>
3 <p id="tagline">Healthy food, made on campus.</p>
4 <p>Today's special: <span id="price">Ragi Idli — ₹30</span></p>
5</body>
·/* Three ids now exist in the tree. Add the one style rule ↓ */
6 #price { color: #333333; font-weight: bold; }
·No id in the HTML = nothing for getElementById to find. These three names are the ONLY handles the buttons below can reach.
REAL OUTPUT — PLAIN HTML FIRST, THEN THE ONE CSS RULE
file:///C:/Users/student/Desktop/fswd-practice/Class-16/campus.html
Poshtik Campus

Healthy food, made on campus.

Today's special: Ragi Idli — ₹30

↑ PLAIN HTML — ALL DEFAULT BLACK, NO CSS YET

Three plain lines grow first. Then the single rule lands and only the price turns grey and bold — the exact look Part 8's JavaScript will overrule.

Handles installed. site-name, tagline, price — now JavaScript has something to grab.

Now pick an id — watch getElementById reach into that live page and grab it

The mini-page on the right is the markup you just wrote. Step the code, then click an id button: JavaScript really runs document.getElementById(...) and the element it grabs lights up in the page. That highlight is the returned element.

script.js — getElementById returns one elementone line per press
1// the HTML has: <h1 id="site-name">…</h1>
2const heading = document.getElementById("site-name");
3console.log(heading); // the <h1> element itself
·one id → one element. Click the id buttons on the right to run this for real against each id.
REAL getElementById — THE GRABBED ELEMENT LIGHTS UP

Poshtik Campus

Healthy food, made on campus.

Today's special: Ragi Idli — ₹30

click an id above to run document.getElementById(...) for real

Try the last button — "discount". There is no element with that id, so getElementById returns null, nothing lights up, and the strip shows null. Remember this: a wrong or missing id gives you null, not an error — the error comes one line later, when you try to edit null. That exact trap is Part 8's debugging drill.

Select the way you already think — in CSS

querySelector & querySelectorAll — grab elements using CSS selectors.

You already know how to point at elements — you did it all through Unit 1 with CSS selectors like .dish, #price, h1. These two methods let JavaScript use that exact same language. document.querySelector(".dish") returns the first match; document.querySelectorAll(".dish") returns every match as a list you can loop over. On a real menu page with 15+ dish cards, that second one hands you all fifteen at once.

First — the markup the selector is aiming at (plain HTML, then two CSS rules)

You cannot select what you cannot see. Before any JavaScript, here is the page those selectors will hit: the same class="dish" from Part 2, just repeated fifteen times (a real menu, like Class 4's). Step it: first the plain HTML rows appear with no styling at all, then the two CSS rules turn them into cards. Only then does the selector question make sense.

menu.html + style.css · plain HTML first, then the two card rulesHTML BUILDS, THEN CSS — ONE PRESS PER LINE
1<body>
2 <div class="dish">Ragi Idli — ₹30</div>
3 <div class="dish">Pesarattu — ₹45</div>
4 <div class="dish">Millet Khichdi — ₹90</div>
5 <!-- ...twelve more .dish rows, fifteen in total -->
6</body>
·/* HTML is complete & plain above — fifteen bare rows. Now the sheet ↓ */
7 .dish { display: inline-block; margin: 4px; } /* sit side by side */
8 .dish { border: 1.5px solid #CBD5E1; padding: 6px 10px; } /* card look */
·Fifteen elements, ONE shared class name. That single repeated label is what makes the next panel's one-liner reach all fifteen.
REAL OUTPUT — PLAIN HTML FIRST, THEN EACH CARD RULE
file:///C:/Users/student/Desktop/fswd-practice/Class-16/menu.html
Ragi Idli — ₹30
Pesarattu — ₹45
Millet Khichdi — ₹90
Sambar Rice — ₹60
Curd Rice — ₹40
↑ PLAIN HTML — BARE DIVS, ONE PER LINE, NO CARDS YET

Same fifteen rows throughout — only the sheet changes. Rule one lets them share a line; rule two draws the border and padding that make them look like cards.

Now the selector question has a target: fifteen elements all wearing class="dish". Next panel points JavaScript at exactly this markup.

Now run a selector against that very menu — feel the difference at scale

The page on the right is the markup you just built, at full size. Step the code, then click a selector: querySelector highlights just the first match; querySelectorAll highlights every match and shows the real count. This is why the "All" version matters — one line reaches fifteen elements.

script.js — first match vs every matchone line per press
1// first match only — one element (or null)
2const first = document.querySelector(".dish");
3
4// EVERY match — a list you can count and loop
5const all = document.querySelectorAll(".dish");
6console.log(all.length); // how many dishes?
·querySelector = the first. querySelectorAll = all of them (a NodeList). Click the buttons on the right.
REAL SELECTORS — RUN AGAINST A FULL MENU
click a selector to run it live and see how many it matches
EXTRA DEPTH · WHICH SELECTOR SHOULD I REACH FOR?

Rule of thumb: if you're grabbing one known, unique thing, getElementById("price") is clearest (and fastest). If you're thinking in CSS, or grabbing many things, use querySelector / querySelectorAll. Note the tiny gotcha: querySelector("#price") needs the # (it's a CSS selector), but getElementById("price") does not (it wants the bare id). Mixing those up is a classic first-week slip.

DEVTOOLS INSPECTION CHALLENGE

Watch the Elements panel update the instant JavaScript changes the DOM.

Here is the single most convincing proof that "the DOM is live, the file is not." On the left is your HTML source — the blueprint on disk, which never changes. On the right is the browser's Elements panel — the live DOM tree. Press Run the script and watch: the source stays frozen, but the Elements panel's text node changes before your eyes. Predict first: which side changes, and which stays the same?

THE SCRIPT THAT WILL RUN
HTML
<h1 id="site-name">Poshtik Campus</h1>
SCRIPT
document.getElementById("site-name").textContent = "Poshtik Campus — Open!";
PREDICT
Does the HTML source file change on disk? Does the Elements panel change? Write your guess, then reveal.

Commit to your prediction first.

Sources (index.html)Elements (live DOM)
index.html — SOURCE ON DISK (never changes)
<h1 id="site-name">Poshtik Campus</h1>
Elements — LIVE DOM (JS just edited it)
<h1 id="site-name">Poshtik Campus</h1>
Press Run the script. Watch only the right (live DOM) node change; the left source stays exactly as written.

The verdict. The source file on disk did not change — reload from disk and you'd get "Poshtik Campus" back. Only the live DOM changed, which is why only the Elements panel updated and the screen redrew. This is the whole mental model of today in one drill: JS edits the live tree, never the file.

Change what the visitor reads

.textContent — read an element's words, or replace them entirely.

Once you're holding an element, .textContent is its words. Read it (heading.textContent) to find out what it says; assign to it (heading.textContent = "…") to replace what it says — and the page updates instantly. It's the DOM twin of the object property you learned yesterday: same dot, same read/write, but now it changes the screen.

Build the page from scratch, then read and replace its heading text

The id="site-name" that JavaScript grabs must exist in HTML first, and the grey it overrules must come from CSS first. So the usual order: plain HTML → CSS → JS. Watch the right-hand page appear with browser defaults, take each CSS rule, then have its heading words replaced by the script.

text.html + style rules + script.js · .textContent, plain HTML → CSS → JSHTML, THEN CSS, THEN JS — ONE PRESS PER LINE
·<!-- FILE 1 of 3 · text.html — the heading whose words we will replace -->
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <title>textContent</title>
6</head>
7<body>
8 <h1 id="site-name">Poshtik Campus</h1>
9 <p class="tip">One real DOM element, above.</p>
10</body>
11</html>
·/* HTML complete & PLAIN above. FILE 2 of 3 · the CSS — one rule per press ↓ */
6 <style>
7 body { font-family: Georgia, serif; padding: 18px; }
8 #site-name { color: #0E7490; font-size: 21px; }
9 .tip { color: #64748B; font-size: 13px; }
10 </style>
·<!-- CSS done. Hire the passenger — last line before </body> -->
11 <script src="script.js"></script>
·// ————— FILE 3 of 3 · script.js — read the words, then replace them —————
1const heading = document.getElementById("site-name");
2console.log(heading.textContent); // read: "Poshtik Campus"
3heading.textContent = "Poshtik Campus — Now Open!";
·read it (line 2), or set it (line 3). Notice the CSS colour SURVIVED the text change — you replaced the words, not the styling.
REAL OUTPUT — PLAIN PAGE, THEN CSS, THEN THE WORDS CHANGE
file:///C:/Users/student/Desktop/fswd-practice/Class-16/text.html

Poshtik CampusPoshtik Campus — Now Open!

One real DOM element, above.

PLAIN HTML — NO CSS AND NO SCRIPT YET
DEVTOOLS · CONSOLE — THE REAL OUTPUT
Poshtik Campusheading.textContent (read)
The <h1> now reads different words — but it kept the teal from #site-name. .textContent swaps the words; the CSS stays exactly where it was.

Now type new words yourself and watch the real heading change

This device is live: every keystroke runs heading.textContent = yourText for real, and the heading below updates instantly. This is exactly how a "live preview" field works on real sites.

REAL .textContent — TYPE AND WATCH IT CHANGE

Poshtik Campus

The heading above is one real DOM element. You are editing its textContent as you type.

heading.textContent = "Poshtik Campus"
EXTRA DEPTH · .textContent vs .innerHTML

.textContent treats what you give it as plain text — safe, and what you want 95% of the time. There's a sibling, .innerHTML, that treats the string as HTML (so "<b>hi</b>" would render bold). It's powerful but risky — feeding user text into .innerHTML is a classic security hole. For changing words, always prefer .textContent.

Restyle the page from JavaScript

element.style.property — change an element's CSS from code.

JavaScript can also change how an element looks. Hold the element, then set element.style.color, element.style.background, element.style.fontSize — each one maps to a CSS property you already know. One tiny naming rule: CSS font-size becomes fontSize in JS (drop the dash, camelCase it), because a dash isn't allowed in a property name. This is how a button turns a total red when a form is invalid, or green when it's saved.

First remember what the CSS already said

This is the moment Part 2's stylesheet earns its place. Our style.css already gave #price a rule — go back and look at lines 11–15 if you need to. So the element arrives on screen grey and bold, decided by CSS. Now JavaScript is about to set .style.color on that same element. Two instructions, one property. Who wins?

TWO SOURCES, ONE PROPERTY — PREDICT THE WINNER
CSS said
#price { color: #333333; }  — style.css, line 13 (from Part 2)
JS says
tag.style.color = "seagreen";  — script.js, running after the page loaded
PREDICT
Does the price end up grey or green? And why — which Unit-1 rule decides it?

The answer, and it's pure Unit 1 cascade. Green wins. When you write element.style.color in JavaScript, the browser does not edit your stylesheet — it writes an inline style straight onto that one element, exactly as if the HTML had said <span id="price" style="color:seagreen">. And you learned in Unit 1 that inline styles sit at the top of the cascade, above id rules, above class rules, above everything in the sheet. So JS-set styles always beat style.css. Useful to know, and also a warning: this is why heavy .style use gets messy fast — you're scattering inline styles that later CSS can never override.

Build it from scratch — watch CSS make it grey, then JavaScript overrule

This is the cascade question made visible, so we build all three files in order: plain HTML, then the CSS that makes the price grey and bold, then the JavaScript that overrules it. Keep your eye on the price tag in the preview: it starts plain black, turns grey when the CSS rule lands, then turns green the moment JS writes an inline style.

style-demo.html + style rules + script.js · .style, plain HTML → CSS → JSHTML, THEN CSS, THEN JS — ONE PRESS PER LINE
·<!-- FILE 1 of 3 · style-demo.html — the price tag JS will restyle -->
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <title>JS .style vs CSS</title>
6</head>
7<body>
8 <h1>Poshtik Campus</h1>
9 <p>Today's special: <span id="price">Ragi Idli — ₹30</span></p>
10</body>
11</html>
·/* HTML complete & PLAIN above. FILE 2 of 3 · the CSS — this is what JS will fight ↓ */
6 <style>
7 body { font-family: Georgia, serif; padding: 18px; }
8 h1 { font-size: 20px; color: #0F172A; }
9 #price { color: #333333; font-weight: bold; }
10 </style>
·<!-- CSS done — the price is GREY now. Hire the passenger -->
11 <script src="script.js"></script>
·// ————— FILE 3 of 3 · script.js — one .style property at a time —————
1const tag = document.getElementById("price");
2tag.style.color = "seagreen"; // beats #price in style.css
3tag.style.fontSize = "22px"; // note: fontSize, not font-size
4tag.style.background = "mintcream"; // a highlight
·CSS font-size → JS fontSize (camelCase). And notice: green won, because .style writes an INLINE style — top of the cascade.
REAL OUTPUT — PLAIN, THEN GREY FROM CSS, THEN GREEN FROM JS
file:///C:/Users/student/Desktop/fswd-practice/Class-16/style-demo.html

Poshtik Campus

Today's special: Ragi Idli — ₹30

PLAIN HTML — NO CSS AND NO SCRIPT YET ✓ INLINE STYLE FROM JS BEAT #price IN style.css
DEVTOOLS · ELEMENTS — WHAT JS ACTUALLY WROTE
<span id="price" style="color: seagreen; font-size: 22px; background: mintcream;">
Three states, in order: plain blackgrey (CSS #price) → green (JS .style.color). JavaScript didn't edit your stylesheet — it stamped an inline style on that one element, which sits at the top of the cascade.

Now recolour it yourself — one real property at a time

This device is live: each button runs one real .style assignment on the price tag below, and you see it recolour, grow, or get a background instantly. Pick a swatch to set the colour to any value.

REAL .style — WATCH THE PRICE TAG RESTYLE
CLICK TO RUN EACH .style LINE

Poshtik Campus

Today's special: Ragi Idli — ₹30

click a button or swatch to run a real .style assignment
BROKEN-SITE DEBUGGING DRILL

The site is blank and the console is red. Find the one-character bug.

This is the error you will hit more than any other in your first month: "Cannot set properties of null (setting 'textContent')". It always means the same thing — getElementById handed you back null because the id you asked for doesn't exist (usually a typo), and then the next line tried to edit null. Read the code, spot the mismatch, and say the fix out loud before you reveal.

THE BROKEN CODE — one id doesn't match
HTML
<h1 id="site-name">Poshtik Campus</h1>
SCRIPT
const h = document.getElementById("sitename");
h.textContent = "Now Open!";
CONSOLE
✗ Uncaught TypeError: Cannot set properties of null (setting 'textContent')
FIND IT
Why is h equal to null? Which line actually throws — the getElementById line, or the next one? What's the fix?

Say the fix out loud first.

First, the page itself — because the bug is a mismatch WITH the HTML

You cannot judge a wrong id without seeing the right one. So here is the whole broken site built from scratch — plain HTML → CSS → JS. Read line 8 carefully: the id is site-name, with a dash. Then watch the script fail on the right.

broken-site.html + style rules + script.js · plain HTML → CSS → JSHTML, THEN CSS, THEN JS — ONE PRESS PER LINE
·<!-- FILE 1 of 3 · broken-site.html — note the EXACT id on line 8 -->
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <title>Poshtik Campus</title>
6</head>
7<body>
8 <h1 id="site-name">Poshtik Campus</h1> <!-- DASH! -->
9 <p class="tip">The heading should say "Now Open!"</p>
10</body>
11</html>
·/* HTML complete & PLAIN above. FILE 2 of 3 · the CSS — one rule per press ↓ */
6 <style>
7 body { font-family: Georgia, serif; padding: 18px; }
8 #site-name { color: #B91C1C; font-size: 21px; }
9 .tip { color: #64748B; font-size: 13px; }
10 </style>
·<!-- CSS done. Hire the passenger — last line before </body> -->
11 <script src="script.js"></script>
·// ————— FILE 3 of 3 · script.js — the BROKEN version —————
1// HTML id is "site-name" (with a dash)
2const h = document.getElementById("sitename"); // ✗ no dash → null
3h.textContent = "Now Open!"; // ✗ THIS line throws
·the heading NEVER changed — it still says "Poshtik Campus". Line 2 silently gave null; line 3 is where it crashed.
REAL OUTPUT — PLAIN, THEN CSS, THEN THE CRASH
file:///C:/Users/student/Desktop/fswd-practice/Class-16/broken-site.html

Poshtik Campus

The heading should say "Now Open!"

PLAIN HTML — NO CSS AND NO SCRIPT YET
DEVTOOLS · CONSOLE — THE REAL OUTPUT
h = nullline 2 did NOT crash
✗ Uncaught TypeError: Cannot set properties of null (setting 'textContent')line 3
The heading above still reads Poshtik Campus — the change never happened. And note where the red appeared: line 2 was silent, line 3 threw.

Now the two scripts, side by side

BROKEN — "sitename" ≠ "site-name"
1// HTML id is "site-name" (with a dash)
2const h = document.getElementById("sitename"); // ✗ no match → null
3h.textContent = "Now Open!"; // ✗ THIS line throws
FIXED — the id now matches exactly
1// HTML id is "site-name" (with a dash)
2const h = document.getElementById("site-name"); // ✓ found it
3h.textContent = "Now Open!"; // ✓ works

The two-step truth. Line 2 does not crash — a bad id quietly returns null. The crash is on line 3, when you read .textContent of null. So the console's line number points at line 3, but the bug is on line 2. Fix rule: when you see Cannot set properties of null, go check the getElementById / querySelector just above it and make its id/selector match the HTML character for character.

One level up from the document

The window object — the whole browser tab, briefly.

If document is the page, window is the tab that holds it — the browser viewport itself. You'll meet it fully later; today just one useful property: window.innerWidth, the width of the viewport in pixels. It connects straight back to Unit 1's media queries — a media query reacts to width in CSS; window.innerWidth reads that same width in JavaScript.

Build the page from scratch — the same width, once in CSS and once in JS

This one has a bonus: the CSS file contains a media query, so you see the same viewport width used twice — first by CSS, then by JavaScript. Usual order: plain HTML → CSS → JS.

viewport.html + style rules + script.js · window.innerWidth, plain HTML → CSS → JSHTML, THEN CSS, THEN JS — ONE PRESS PER LINE
·<!-- FILE 1 of 3 · viewport.html — the page that reads its own width -->
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <title>window.innerWidth</title>
6</head>
7<body>
8 <h1>How wide is this tab?</h1>
9 <p class="tip">CSS reacts to the width; JS can read it.</p>
10</body>
11</html>
·/* HTML complete & PLAIN above. FILE 2 of 3 · the CSS — one rule per press ↓ */
6 <style>
7 body { font-family: Georgia, serif; padding: 18px; }
8 h1 { color: #B45309; font-size: 20px; }
9 .tip { color: #64748B; font-size: 13px; }
10 </style>
·<!-- CSS done. Hire the passenger — last line before </body> -->
11 <script src="script.js"></script>
·// ————— FILE 3 of 3 · script.js — reading the viewport width —————
1// window = the browser tab itself
2console.log(window.innerWidth); // viewport width in px
·the same width a CSS media query watches — now readable as a NUMBER from JS. Press the button below to read it live.
REAL OUTPUT — PLAIN PAGE, THEN CSS, THEN THE WIDTH READ
file:///C:/Users/student/Desktop/fswd-practice/Class-16/viewport.html

How wide is this tab?

CSS reacts to the width; JS can read it.

PLAIN HTML — NO CSS AND NO SCRIPT YET
DEVTOOLS · CONSOLE — THE REAL OUTPUT
1280window.innerWidth (a number, not "1280px")
Note it printed 1280, not "1280px" — a plain number you can do maths with. That's the difference between CSS reacting to width and JS reading it.

Now read YOUR browser's real width, live

Press the button: it runs window.innerWidth for real and prints this browser's actual current width. Resize your window and press again — the number changes, because it genuinely reads the live viewport.

CONSOLE — the REAL viewport width
// press the button — this reads your actual browser width
[SELF-STUDY] · WINDOW METHODS QUICK-REFERENCE — alert(), confirm()
CallWhat it doesReturns
alert("Saved!")pops a message box the user must dismissnothing (undefined)
confirm("Delete?")pops an OK / Cancel boxtrue (OK) or false (Cancel)
window.innerWidthcurrent viewport width in pixelsa number, e.g. 1280
window.innerHeightcurrent viewport height in pixelsa number, e.g. 720

These are window methods, so you can write window.alert(...) or just alert(...)window is the default object, so it's optional. Skim this now; you'll use confirm() for a real "are you sure?" in a later lab.

CONSOLE-ERROR DIAGNOSIS

The script runs too early — and reads an element that isn't there yet.

A subtler cousin of Part 9's bug, and the reason so many first scripts "just don't work." The id is spelled perfectly — but the <script> sits in the <head>, so it runs before the browser has built the <body>. At that moment the element genuinely doesn't exist yet, so getElementById returns null again. Diagnose the timing, not the spelling.

Here is the whole broken file, from scratch — because with a timing bug where the tag sits IS the bug, and you can only see that in the complete file. Read it top to bottom the way the browser does, and mark the exact instant the script runs.

broken.html · complete file — the id is PERFECTREAD IN BROWSER ORDER
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <title>Poshtik Campus</title>
5 <script> <!-- ✗ the bug: this runs NOW, in the head -->
6 const t = document.getElementById("tagline"); // spelling is CORRECT
7 t.textContent = "Hi"; // ✗ throws here — t is null
8 </script>
9</head>
10<body>
11 <p id="tagline">Healthy food</p> <!-- born too LATE -->
12</body>
13</html>
·the browser reads 1→13 in order. At line 6 it has not yet reached line 11, so the p genuinely does not exist. Same id, different TIME.
DevTools · Console
// line 6 ran — no error yet, t is quietly null
✗ Uncaught TypeError: Cannot set properties of null (setting 'textContent')
    at broken.html:7
// page renders "Healthy food" unchanged — the script died before it could edit anything
DIAGNOSE BEFORE YOU REVEAL
DIAGNOSE
The id matches perfectly this time. So why is t still null? What are the two standard fixes?

Name the cause first — it's timing, not spelling.

Cause: the script in the <head> runs while the browser is still reading top-to-bottom — the <body> and its <p id="tagline"> don't exist yet, so getElementById returns null. Fix 1 (simplest, and what the labs use): move the <script> to the very bottom of the <body>, after all the elements. Fix 2: keep it where it is but add defer<script src="script.js" defer> — which tells the browser "run this only after the whole page is built." Either way, the rule is: your DOM code must run after the DOM exists.

Here is Fix 1 as the complete file, built in the usual order — plain HTML → CSS → JS — so you can watch the page arrive plain, take its styling, and only then get its script. The only difference from the broken version is where those script lines sit. That move is the entire fix.

fixed.html · complete file — plain HTML → CSS → JS, script at the bottomHTML, THEN CSS, THEN JS — ONE PRESS PER LINE
·<!-- PLAIN HTML first — nothing but structure -->
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <title>Poshtik Campus</title> <!-- head is now script-free -->
5</head>
6<body>
7 <h1>Poshtik Campus</h1>
8 <p id="tagline">Healthy food</p> <!-- ✓ born FIRST -->
9</body>
10</html>
·/* HTML complete & PLAIN above. Now the CSS — one rule per press ↓ */
5 <style>
6 body { font-family: Georgia, serif; padding: 18px; }
7 h1 { color: #047857; font-size: 20px; }
8 #tagline { color: #334155; font-size: 15px; }
9 </style>
·<!-- CSS done. NOW the script — and note WHERE it goes -->
10 <script> <!-- ✓ last thing in body -->
11 const t = document.getElementById("tagline"); // finds it
12 t.textContent = "Hi"; // ✓ works
13 </script>
·nothing was added to the LOGIC — the script block simply moved from the head to the end of the body. By the time it runs, #tagline already exists.
REAL OUTPUT — PLAIN, THEN CSS, THEN THE SCRIPT SUCCEEDS
file:///C:/Users/student/Desktop/fswd-practice/Class-16/fixed.html

Poshtik Campus

Healthy foodHi

PLAIN HTML — NO CSS AND NO SCRIPT YET
DEVTOOLS · CONSOLE — THE REAL OUTPUT
✓ no errors — the element was found
✓ Console clean, no red. The paragraph really says Hi — the script found the element because it existed by then. Same code as the broken file; later moment.

Fix 2, in one line, for the record. If you genuinely need the script in the <head> (real projects sometimes do), keep it there and add the defer attribute to an external script: <script src="script.js" defer></script>. defer means "download it now, but don't run it until the whole page is built." Note the catch: defer works on external scripts with a src — it has no effect on an inline <script> block like line 8 above. That's one more reason our template always uses an external script.js.

Why this matters for Lab 4. Every script you write from Lab 4 onward reads from the page. If you ever see null on an id you know is spelled right, this timing bug is the first suspect — check where your <script> tag sits. That's why our lab template always puts <script src="script.js"> at the bottom of <body> — exactly as you saw on line 17 of Part 2's campus.html.

Class 16 · wrap-up

You can now reach into a live page and change it. That's the whole job of front-end JavaScript.

Selecting, reading, writing text, restyling, and reading the two most common errors — that is the core loop every interactive site runs a thousand times a second. Lab 4 puts it to real use: your script will read the Poshtik Campus order form and model a dish as an object.

Take-home kit — the DOM cheat-card

Copy this into your notes. It answers every DOM question you saw today at a glance.

  • The DOM: the browser's live, in-memory tree of your HTML — one node per tag. JS edits the tree, never the file.
  • Select: document.getElementById("price") (bare id, one element) · document.querySelector(".dish") (CSS, first) · document.querySelectorAll(".dish") (CSS, all).
  • Change text: el.textContent = "…" reads/replaces an element's words.
  • Change style: el.style.color = "seagreen" · remember CSS font-size → JS fontSize.
  • The #1 error: Cannot set properties of null → the id/selector didn't match, OR the script ran before the DOM was built. Put <script> at the bottom of <body>.

Folder state before Lab 4 — where script.js lives now

Nothing changed on disk today (every demo ran on this slide). But here is the state your real poshtik-campus\ project is in as you walk into Lab 4 — script.js already exists from earlier labs and is linked at the bottom of each page.

poshtik-campus\ — STATE ENTERING LAB 4
poshtik-campus\
index.html
menu.html
style.css
script.js  ← learns to read the form in Lab 4
Reminder: this project lives only in the lab classes. Today's fswd-practice\Class-16\ scratch folder is throwaway and never committed.
ClassTopicWhat script.js could do by then
13What is JS + first scriptprint to the console
14Variables & data typeshold values, know their types
15Objectsmodel one dish as an object
16The DOM (today)select elements, change text & style
Before you leave — say these out loud (self-test)
  1. Define the DOM in one sentence, using the words "live" and "tree".
  2. Write the line that grabs the element with id price two different ways.
  3. Change that element's words, then turn its text green — write both lines.
  4. You see Cannot set properties of null. Name the two things that could cause it.
  5. Where should a <script> that touches the DOM be placed, and why?