The DOM (Document Object Model) is a programming interface for HTML documents. When a browser loads a web page, it creates a tree-like representation of the HTML that JavaScript can interact with. This lets you make pages dynamic and interactive.
<!-- HTML -->
<div id="container">
<h1>Hello</h1>
<p>Welcome</p>
</div>
DOM Tree:
div#container
├─ h1
│ └─ "Hello"
└─ p
└─ "Welcome"
Every HTML element becomes a "node" in this tree that JavaScript can find, modify, create, or remove. This is how websites respond to clicks, update content without reloading, and create interactive experiences.

Visual representation of the DOM
<aside> 💡
The browser parses your HTML and creates the DOM automatically. When you use JavaScript to change the DOM, the browser immediately updates what you see on the page - no refresh needed.
</aside>
https://www.youtube.com/watch?v=NO5kUNxGIu0
<button id="myButton">Click me</button>
<script>
// 1. Find the button in the DOM
const button = document.querySelector('#myButton');
// 2. Add interactivity
button.addEventListener('click', function() {
button.textContent = 'Clicked!';
});
</script>
When you click the button, JavaScript modifies the DOM, and the browser updates the display instantly.
<aside> ⌨️
Hands On
Open DevTools Console on any webpage and type: document.querySelector('h1').textContent = 'I changed this!' - watch the heading change instantly. This is DOM manipulation in action.
</aside>
window ObjectWhen JavaScript runs in a browser, it has access to two important global objects: window and document. Understanding the difference helps you know which one to reach for.
window is the top-level object representing the browser tab itself. It contains everything the browser exposes to JavaScript — including document. Any variable you declare at the top level of a script becomes a property of window.
document is a property of window that represents the HTML page loaded in that tab. It's your entry point for reading and modifying the page's content and structure.
// window = the browser tab
console.log(window.innerWidth); // width of the browser window in px
console.log(window.innerHeight); // height of the browser window in px
// document = the HTML page inside the tab
console.log(document.title); // the page's <title> text
console.log(document.body); // the <body> element
// window is the global object — these are identical:
window.alert('Hello');
alert('Hello');
// document is a property of window — these are identical:
window.document.querySelector('h1');
document.querySelector('h1');
Common window methods you'll use:
// Alerts and confirmations
alert('Something happened!');
const confirmed = confirm('Are you sure?'); // returns true/false
// Timing
setTimeout(() => {
console.log('Runs once after 2 seconds');
}, 2000);
setInterval(() => {
console.log('Runs every 1 second');
}, 1000);
// Current page URL and navigation
console.log(window.location.href); // full URL of current page
<aside> 💡
Info
In practice, you write document.querySelector() not window.document.querySelector() — the window. prefix is always optional because window is the global scope. But knowing window exists explains why alert(), setTimeout(), and location just work without any import or setup.
</aside>
Before you can manipulate elements, you need to find them in the DOM.
Returns the first element that matches the selector:
// By ID
const header = document.querySelector('#main-header');
// By class
const button = document.querySelector('.btn');
// By element type
const paragraph = document.querySelector('p');
// By complex selector
const firstLink = document.querySelector('nav a');
// If no match found, returns null
const missing = document.querySelector('.nonexistent'); // null
Returns a NodeList (like an array) of all matching elements:
// All paragraphs
const paragraphs = document.querySelectorAll('p');
// All elements with class "card"
const cards = document.querySelectorAll('.card');
// All links inside nav
const navLinks = document.querySelectorAll('nav a');
// Loop through results
paragraphs.forEach(function(p) {
console.log(p.textContent);
});
Older method, still commonly used. Only works with IDs:
const container = document.getElementById('container');
// Same as: document.querySelector('#container')
<aside> 💡
querySelector() is more flexible because it accepts any (CSS) selector. Use querySelector() for single elements and querySelectorAll() for multiple elements.
</aside>
// Select first button
const btn = document.querySelector('button');
// Select all list items
const items = document.querySelectorAll('li');
// Select element by ID
const output = document.querySelector('#output');
// Select nested elements
const firstCardTitle = document.querySelector('.card h2');
<aside> ⌨️
Hands On
Create an HTML file with 3 paragraphs, each with class "text". Use querySelectorAll('.text') to select them all and log each one's text content in the console.
</aside>
Once you've selected elements, you can change their content and appearance.
Updates the text inside an element:
const heading = document.querySelector('h1');
// Read current text
console.log(heading.textContent); // "Original Heading"
// Change text
heading.textContent = 'New Heading';
Updates the HTML inside an element:
const container = document.querySelector('#container');
// Read current HTML
console.log(container.innerHTML); // "<p>Hello</p>"
// Change HTML
container.innerHTML = '<h2>New Title</h2><p>New paragraph</p>';
<aside> ⚠️
Never use innerHTML with user input - it can execute malicious scripts. Always use textContent for user-provided content. Use innerHTML only when you control the HTML content yourself.
</aside>
You can dynamically build and remove elements from the page — this is how interactive UIs add items to lists, show notifications, and update content without a page reload.
// Step 1: create the element
const newItem = document.createElement('li');
// Step 2: set its content and any attributes
newItem.textContent = 'Buy oat milk';
newItem.classList.add('shopping-item');
// Step 3: add it to the page
const list = document.querySelector('#shopping-list');
list.appendChild(newItem);
appendChild always adds the new element as the last child of the target. If you need to insert it at a specific position, use insertBefore or the more flexible insertAdjacentElement.
const fruits = ['Apple', 'Mango', 'Banana'];
const list = document.querySelector('#fruit-list');
fruits.forEach((fruit) => {
const item = document.createElement('li');
item.textContent = fruit;
list.appendChild(item);
});
<aside> ⚠️
Warning
Avoid calling appendChild inside a loop on a large list — each call triggers a browser repaint. For long lists, build a DocumentFragment first and append it once. For the exercises in this course, a simple forEach loop is perfectly fine.
</aside>
children propertyOnce elements are in the DOM, you can read back what's inside a parent using the children property. It returns an HTMLCollection of all direct child elements.
const list = document.querySelector('#shopping-list');
// Read all child elements
console.log(list.children); // HTMLCollection of <li> elements
console.log(list.children.length); // how many items are in the list
// Access a specific child by index
console.log(list.children[0]); // first child
console.log(list.children[list.children.length - 1]); // last child
// Loop over children (convert to array first for .forEach)
Array.from(list.children).forEach((child) => {
console.log(child.textContent);
});
<aside> 💡
Info
children only returns element nodes — it skips text nodes and comments. If you need everything including text nodes, use childNodes instead. For most practical purposes, children is what you want.
</aside>
// Remove a specific element
const item = document.querySelector('.remove-me');
item.remove();
// Remove the first child of a parent
const list = document.querySelector('#shopping-list');
list.children[0].remove();
// Remove all children at once
list.innerHTML = '';
<aside> ⚠️
Warning
list.innerHTML = '' is the fastest way to clear all children, but it also destroys any event listeners attached to those children. If you've added event listeners to individual items, remove them one by one using item.remove() inside a loop instead.
</aside>
<aside> ⌨️
</aside>
Event listeners make your page respond to user actions.
Attach functions that run when events occur:
// Syntax
element.addEventListener('eventType', function() {
// Code to run when event happens
});
// Example
const button = document.querySelector('#myButton');
button.addEventListener('click', function() {
console.log('Button was clicked!');
});
Mouse events:
// Click
element.addEventListener('click', function() { });
// Double click
element.addEventListener('dblclick', function() { });
// Mouse enter/leave (for hover effects)
element.addEventListener('mouseenter', function() { });
element.addEventListener('mouseleave', function() { });
Keyboard events:
// Any key pressed
document.addEventListener('keydown', function(event) {
console.log('Key pressed:', event.key);
});
// Specific key
document.addEventListener('keydown', function(event) {
if (event.key === 'Enter') {
console.log('Enter was pressed!');
}
});
Form events:
const form = document.querySelector('form');
// Form submitted
form.addEventListener('submit', function(event) {
event.preventDefault(); // Prevent page reload
console.log('Form submitted!');
});
const input = document.querySelector('input');
// Input value changed
input.addEventListener('input', function() {
console.log('Current value:', input.value);
});
Form handling: