The DOM is the browser's runtime (while the page is loaded) in-memory (temporarily stored in RAM) representation of parsed HTML markup, accessible and modifiable via JavaScript APIs.
Fundamental distinction: HTML source files are static; the DOM is a dynamic, mutable (changeable) object tree instantiated (created) by the browser.
- HTML file β Static markup file on disk (
.html) - DOM β Runtime object tree in browser memory
Instantiation flow:
- Browser fetches and parses (reads and breaks down) HTML markup
- Parser constructs DOM tree corresponding to element hierarchy
- Browser renders tree to viewport
- JavaScript can now query and mutate (change) DOM nodes at runtime
Relevance: JavaScript operates exclusively on DOM nodes, not the source HTML file. All DOM modifications are transient (temporary) unless persisted (saved) server-side.
| HTML | DOM |
|---|---|
Static file (.html) |
Live in-memory structure |
| What you write | What the browser created from your HTML |
| Never changes unless you reload | Changes every time JavaScript runs |
| Text-based | JavaScript object tree |
Example:
<!-- HTML file -->
<div id="message">Hello</div>// JavaScript manipulation (changes the DOM, not the HTML file)
const div = document.getElementById("message");
div.textContent = "Goodbye"; // DOM changed, but HTML file unchangedClose the laptop and reopen β HTML says "Hello" (unchanged) Reload the page β DOM gets recreated from HTML file (says "Hello")
Document Object Model = Objects arranged in a tree structure
document (root)
βββ html
β βββ head
β β βββ title
β β βββ link (stylesheet)
β βββ body
β βββ header
β βββ main
β β βββ h1
β β βββ p
β β βββ button
β βββ footer
Each item is called a node. Nodes can be:
- Parent: Contains other nodes
- Child: Contained by another node
- Sibling: Have the same parent
Why this matters? To find elements in the DOM, you navigate this tree.
- This method validates whether a value is an array instance, distinct from array-like objects
- Returns boolean indicating array status
Array.isArray([1, 2, 3]); // true
Array.isArray("hello"); // false
Array.isArray({}); // falseWhy? Arrays are technically objects in JavaScript. Array.isArray() specifically checks if something is an array.
- DOM = Document Object Model
- It is the browser's runtime representation of parsed HTML markup.
| HTML | DOM |
|---|---|
| Static markup file | Runtime object tree in browser memory |
| Source code on disk | Parsed and instantiated structure |
| Immutable (without server reload) | Mutable via JavaScript APIs |
| Text-based | Interconnected node objects |
Getting elements:
// Get by ID (most specific)
const elem = document.getElementById("myId");
// Get by class (returns multiple)
const elems = document.getElementsByClassName("myClass");
// Modern way (more flexible)
const elem = document.querySelector("#myId"); // ID
const elems = document.querySelectorAll(".myClass"); // Class
const elem = document.querySelector("div"); // Tag
const elem = document.querySelector("[data-id='123']"); // Attribute
// Traverse the tree
const parent = element.parentNode;
const children = element.children; // Only direct children
const firstChild = element.firstChild;
const siblings = element.previousSibling / nextSibling;Best practice: Use querySelector/querySelectorAll - they're modern and flexible.
const parent = document.getElementById("container");
const children = parent.children; // HTMLCollection (array-like)
children.forEach((child) => {
console.log(child);
});// Old way (verbose)
const item = document.getElementById("to-remove");
item.parentNode.removeChild(item);
// Modern way (simpler)
item.remove();// Change text content
element.textContent = "New text";
// Change HTML (be careful with user input!)
element.innerHTML = "<strong>Bold</strong>";
// Update attributes
element.setAttribute("src", "image.jpg");
element.id = "newId";
// Add/remove classes
element.classList.add("active");
element.classList.remove("inactive");
element.classList.toggle("highlight"); // Toggle on/off
// Change styles
element.style.color = "red";
element.style.backgroundColor = "blue";Many DOM collections (children, NodeList) look like arrays but aren't:
// β This doesn't work (not a real array)
const children = parent.children;
children.map(child => ...); // Error! map is not a function
// β
Convert to real array
const childrenArray = Array.from(parent.children);
childrenArray.forEach(child => {
console.log(child);
});
// β
Or use spread operator
const childrenArray = [...parent.children];Events represent user interactions and browser lifecycle changes that an application can subscribe to (listen for) and handle.
Event types:
clickβ Mouse button clicked on elementsubmitβ Form submission triggeredinput/changeβ Form input value changedkeydown/keyupβ Keyboard key pressed/releasedmouseover/mouseoutβ Mouse enters/leaves element boundaryloadβ Document fully parsed and resources loadedscrollβ Viewport scrolled
User Interaction (click element)
β
Browser Event System fires event
β
Executes attached event listeners
β
Listeners invoke callback functions
β
Application logic executes
β
DOM/State modifications occur
element.addEventListener("click", (event) => {
// Callback execution triggered on event emission
});Event handler = Callback function registered as listener. Executes asynchronously (independently, when event occurs) upon event occurrence.
button.addEventListener("click", (event) => {
console.log("Clicked!");
console.log(event.target); // The element that was clicked
});const form = document.querySelector("form");
form.addEventListener("submit", (event) => {
event.preventDefault(); // Stop page reload
const input = form.querySelector("input");
console.log("User submitted:", input.value);
});const input = document.querySelector("input");
input.addEventListener("input", (event) => {
console.log("User typed:", event.target.value);
});element.addEventListener("click", (event) => {
event.target; // The element clicked
event.type; // "click", "submit", etc.
event.preventDefault(); // Stop default behavior
event.stopPropagation(); // Stop event bubbling
});Understanding the architecture:
Client = Browser environment with JavaScript runtime Server = Backend process managing persistent state and business logic
- β Change DOM, update UI instantly
- β Handle events (clicks, typing, etc.)
- β No page reload needed for changes
- β Limitation: Can't access server databases directly
// Client can do this (instantly)
document.getElementById("message").textContent = "You won!";- β Store persistent data in databases
- β Run business logic safely
- β Handle authentication & security
- β Process requests from multiple clients
- β Can't directly manipulate user's DOM
Browser (Client) Server
| |
|-- HTTP Request ------->|
| (e.g., GET /friends) |
| |
| Query Database |
|<------ Response --------|
| (JSON data) |
| |
Update DOM
User sees results
Key point: After the page loads, the server doesn't know what the client does with JavaScript. They communicate by making explicit requests.
// Client-side: Handle button click
button.addEventListener("click", async () => {
// 1. Send request to server
const response = await fetch("/api/friends");
// 2. Server processes
// (Client doesn't know what's happening)
// 3. Server sends back data
const friends = await response.json();
// 4. Client updates DOM with the data
document.getElementById("friendsList").innerHTML = friends
.map((f) => `<div>${f.name}</div>`)
.join("");
});Timeline:
- User clicks button β Client-side function runs
- Client sends HTTP request to server
- Server fetches from database
- Server sends response back
- Client receives response β Updates DOM
- User sees the result
Important distinction:
- Server has no control over what you do after it sends the response
- Everything you do with JavaScript (without making another request) is temporary and only affects that user's browser
- If user refreshes page β DOM goes back to original state
<!-- HTML -->
<input id="todoInput" type="text" placeholder="Add a todo..." />
<button id="addBtn">Add</button>
<ul id="todoList"></ul>// JavaScript (Client-side)
const input = document.getElementById("todoInput");
const addBtn = document.getElementById("addBtn");
const list = document.getElementById("todoList");
addBtn.addEventListener("click", async () => {
// 1. Get the text from input (DOM manipulation)
const text = input.value;
// 2. Send to server (request)
const response = await fetch("/api/todos", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ text }),
});
// 3. Get response from server
const newTodo = await response.json();
// 4. Update DOM (client-side rendering)
const li = document.createElement("li");
li.textContent = newTodo.text;
li.id = `todo-${newTodo.id}`;
list.appendChild(li);
// 5. Clear input
input.value = "";
});What's happening:
- User types something and clicks "Add"
- Client reads input value from DOM
- Client sends request to server
- Server creates todo in database
- Server sends back the created todo
- Client adds it to the DOM (visible immediately)
- User sees the new todo without page reload
-
Cache DOM selections β Don't query the same element repeatedly
// β Bad for (let i = 0; i < 100; i++) { document.getElementById("counter").textContent = i; } // β Good const counter = document.getElementById("counter"); for (let i = 0; i < 100; i++) { counter.textContent = i; }
-
Use event delegation β Attach listeners to parent, not children
// β Good for many items document.getElementById("list").addEventListener("click", (e) => { if (e.target.matches(".item")) { console.log("Clicked item:", e.target); } });
-
Clean up event listeners β Prevent memory leaks (especially important in React)
button.addEventListener("click", handler); // Later, when needed: button.removeEventListener("click", handler);
-
Prefer
textContentoverinnerHTMLβ Safer from XSS attacks// β Unsafe (user input could have malicious HTML) element.innerHTML = userInput; // β Safe (just text, no HTML parsing) element.textContent = userInput;
| Task | Code |
|---|---|
| Select element | document.querySelector("#id") |
| Select all | document.querySelectorAll(".class") |
| Change text | element.textContent = "new text" |
| Add element | parent.appendChild(newElement) |
| Remove element | element.remove() |
| Add class | element.classList.add("active") |
| Add event listener | element.addEventListener("click", handler) |
| Get input value | input.value |
| Change CSS | element.style.color = "red" |
- Forgetting
.addEventListener()- Event listeners need to be attached - Using
innerHTMLwith user input - XSS vulnerability - Querying DOM in loops - Cache the selection first
- Forgetting to prevent default behavior - Use
event.preventDefault() - Not handling errors - Always wrap async code in try/catch