JavaScript DOM Manipulation & Browser APIs: Complete Guide to Interactive Web Development


Welcome back to our comprehensive JavaScript series! In Part 1, we covered the fundamental building blocks of JavaScript. Now it's time to bring your JavaScript knowledge to life by learning how to interact with web pages, handle user events, and create dynamic, interactive experiences.
This second part focuses on the browser-side JavaScript that makes websites interactive. You'll learn how to manipulate HTML elements, respond to user actions, validate forms, manage timers, and store data in the browser. By the end of this guide, you'll be able to build fully interactive web applications.
Table of Contents
The DOM (Document Object Model) 🌍
The DOM is your gateway to making web pages interactive. Think of it as the live representation of your HTML that JavaScript can read and modify. When JavaScript changes the DOM, the browser immediately updates what users see.
html
<!DOCTYPE html>
<html>
<head><title>My Page</title></head>
<body>
<div id="container">
<h1 class="title">Welcome!</h1>
<p class="text">Hello World</p>
</div>
</body>
</html>
The DOM represents this as a tree structure:
Document (root)
html element
head element
- title element (text: "My Page")
body element
div element (id="container")
h1 element (class="title", text: "Welcome!")
p element (class="text", text: "Hello World")
Selecting DOM Elements
Before you can manipulate elements, you need to select them:
Javascript
// Select by ID (returns single element)
const container = document.getElementById('container');
// Select by class name (returns HTMLCollection)
const titles = document.getElementsByClassName('title');
// Select by tag name
const paragraphs = document.getElementsByTagName('p');
// Modern selectors (recommended)
const title = document.querySelector('.title'); // First match
const allTexts = document.querySelectorAll('.text'); // All matches
// Advanced selectors
const firstParagraph = document.querySelector('div > p:first-child');
const inputs = document.querySelectorAll('input[type="email"]');
Accessing and Modifying Content
Javascript
const element = document.querySelector('.title');
// Reading content
console.log(element.innerText); // "Welcome!" (visible text only)
console.log(element.textContent); // "Welcome!" (all text, including hidden)
console.log(element.innerHTML); // "Welcome!" (includes HTML tags)
// Modifying content
element.innerText = "New Title"; // Sets text content
element.innerHTML = "<em>Styled Title</em>"; // Sets HTML content
// Working with attributes
element.getAttribute('class'); // Gets attribute value
element.setAttribute('data-id', '123'); // Sets attribute
element.removeAttribute('class'); // Removes attribute
// Modern property access
console.log(element.id); // Direct property access
element.className = "new-class"; // Set class directly
Dynamic DOM Manipulation
Creating and modifying elements on the fly:
Javascript
// Create new elements
const newDiv = document.createElement('div');
const newText = document.createTextNode('Dynamic content');
// Build element structure
newDiv.appendChild(newText);
newDiv.className = 'dynamic-element';
newDiv.setAttribute('data-created', 'true');
// Add to DOM
document.body.appendChild(newDiv); // Add to end
container.prepend(newDiv); // Add to beginning
container.insertBefore(newDiv, firstChild); // Insert at specific position
// Remove elements
const elementToRemove = document.querySelector('.old-element');
elementToRemove.remove(); // Modern way
// Or: elementToRemove.parentNode.removeChild(elementToRemove); // Old way
// Replace elements
const newElement = document.createElement('span');
oldElement.replaceWith(newElement);
Styling Elements
Javascript
const element = document.querySelector('.my-element');
// Direct style manipulation
element.style.color = 'red';
element.style.backgroundColor = 'yellow';
element.style.fontSize = '20px';
// Working with CSS classes (preferred method)
element.classList.add('highlight'); // Add class
element.classList.remove('old-style'); // Remove class
element.classList.toggle('active'); // Toggle class
element.classList.contains('highlight'); // Check if class exists
// Multiple classes
element.classList.add('class1', 'class2', 'class3');
DOM Traversal
Navigate between related elements:
Javascript
const element = document.querySelector('.current');
// Parent relationships
console.log(element.parentNode); // Direct parent
console.log(element.parentElement); // Parent element (similar)
console.log(element.closest('.container')); // Nearest ancestor with class
// Child relationships
console.log(element.children); // Direct child elements
console.log(element.firstElementChild); // First child element
console.log(element.lastElementChild); // Last child element
// Sibling relationships
console.log(element.nextElementSibling); // Next sibling
console.log(element.previousElementSibling); // Previous sibling
⚠️ Common Confusion:
innerHTML vs textContent vs innerText:
Javascript
const div = document.querySelector('div');
div.innerHTML = '<strong>Bold</strong> text';
console.log(div.innerHTML); // "<strong>Bold</strong> text"
console.log(div.textContent); // "Bold text" (no HTML)
console.log(div.innerText); // "Bold text" (respects CSS visibility)
Live HTMLCollection vs Static NodeList:
Javascript
// Live collection (updates automatically)
const liveList = document.getElementsByClassName('item');
// Static collection (snapshot)
const staticList = document.querySelectorAll('.item');
// If you add a new element with class 'item':
// liveList.length will increase automatically
// staticList.length stays the same
2. Events and Event Handling 🎯
Events are actions that happen in the browser - user clicks, form submissions, page loads, etc. JavaScript can "listen" for these events and respond accordingly.
Key Concepts:
- Event binding:
Javascript
button.addEventListener("click", () => alert("Clicked!"));
Common events:
click
,input
,change
,submit
,mouseover
,keyup
Event object →
event.target
,event.type
,event.preventDefault()
Event bubbling & capturing
Event delegation
Adding Event Listeners
Javascript
const button = document.querySelector('#my-button');
// Modern way (recommended)
button.addEventListener('click', function(event) {
console.log('Button clicked!');
console.log('Event type:', event.type);
console.log('Target element:', event.target);
});
// Arrow function syntax
button.addEventListener('click', (event) => {
console.log('Clicked with arrow function!');
});
// Named function (reusable)
function handleClick(event) {
console.log('Handling click event');
}
button.addEventListener('click', handleClick);
// Remove event listener
button.removeEventListener('click', handleClick);
Common Event Types
Javascript
const input = document.querySelector('#email');
const form = document.querySelector('#my-form');
// Input events
input.addEventListener('input', (e) => {
console.log('User is typing:', e.target.value);
});
input.addEventListener('change', (e) => {
console.log('Input value changed:', e.target.value);
});
input.addEventListener('focus', () => {
console.log('Input focused');
});
input.addEventListener('blur', () => {
console.log('Input lost focus');
});
// Form events
form.addEventListener('submit', (e) => {
e.preventDefault(); // Prevent default form submission
console.log('Form submitted');
});
// Mouse events
button.addEventListener('mouseover', () => {
console.log('Mouse over button');
});
button.addEventListener('mouseout', () => {
console.log('Mouse left button');
});
// Keyboard events
document.addEventListener('keydown', (e) => {
console.log('Key pressed:', e.key);
if (e.key === 'Escape') {
console.log('Escape key pressed!');
}
});
Event Bubbling and Capturing
Javascript
// HTML: <div id="parent"><button id="child">Click me</button></div>
const parent = document.querySelector('#parent');
const child = document.querySelector('#child');
// Bubbling phase (default) - inside out
child.addEventListener('click', () => console.log('Child clicked'));
parent.addEventListener('click', () => console.log('Parent clicked'));
// Output when child is clicked: "Child clicked", "Parent clicked"
// Capturing phase - outside in
parent.addEventListener('click', () => console.log('Parent captured'), true);
child.addEventListener('click', () => console.log('Child captured'), true);
// Output: "Parent captured", "Child captured"
// Stop bubbling
child.addEventListener('click', (e) => {
e.stopPropagation();
console.log('Child clicked - no bubbling');
});
Event Delegation
Instead of adding listeners to many elements, use delegation:
Javascript
// Instead of this (inefficient for many items):
const buttons = document.querySelectorAll('.item-button');
buttons.forEach(button => {
button.addEventListener('click', handleItemClick);
});
// Use delegation (efficient):
const container = document.querySelector('.items-container');
container.addEventListener('click', (e) => {
// Check if clicked element has the class we want
if (e.target.classList.contains('item-button')) {
handleItemClick(e);
console.log('Item ID:', e.target.dataset.itemId);
}
});
function handleItemClick(event) {
console.log('Item clicked:', event.target);
}
3. Forms and Form Validation 📝
Forms are the backbone of user interaction – from logins to feedback submissions.
Key Concepts:
Reading Form Values
html
<form id="user-form">
<input type="text" id="username" name="username" placeholder="Username">
<input type="email" id="email" name="email" placeholder="Email">
<textarea id="message" name="message" placeholder="Message"></textarea>
<select id="country" name="country">
<option value="us">United States</option>
<option value="uk">United Kingdom</option>
</select>
<input type="checkbox" id="newsletter" name="newsletter">
<button type="submit">Submit</button>
</form>
Javascript
const form = document.querySelector('#user-form');
form.addEventListener('submit', (e) => {
e.preventDefault(); // Prevent default form submission
// Reading individual values
const username = document.querySelector('#username').value;
const email = document.querySelector('#email').value;
const message = document.querySelector('#message').value;
const country = document.querySelector('#country').value;
const newsletter = document.querySelector('#newsletter').checked;
// Using FormData (modern approach)
const formData = new FormData(form);
const data = Object.fromEntries(formData.entries());
console.log('Form data:', data);
});
// Real-time validation
const emailInput = document.querySelector('#email');
emailInput.addEventListener('input', (e) => {
validateEmail(e.target.value);
});
Form Validation
javascript
function validateForm() {
const username = document.querySelector('#username').value.trim();
const email = document.querySelector('#email').value.trim();
const errors = [];
// Username validation
if (username.length < 3) {
errors.push('Username must be at least 3 characters');
showError('username', 'Username too short');
} else {
clearError('username');
}
// Email validation
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
errors.push('Please enter a valid email');
showError('email', 'Invalid email format');
} else {
clearError('email');
}
return errors.length === 0;
}
function showError(fieldId, message) {
const field = document.querySelector(`#${fieldId}`);
const errorElement = document.querySelector(`#${fieldId}-error`) ||
createErrorElement(fieldId);
field.classList.add('error');
errorElement.textContent = message;
errorElement.style.display = 'block';
}
function clearError(fieldId) {
const field = document.querySelector(`#${fieldId}`);
const errorElement = document.querySelector(`#${fieldId}-error`);
field.classList.remove('error');
if (errorElement) {
errorElement.style.display = 'none';
}
}
function createErrorElement(fieldId) {
const errorDiv = document.createElement('div');
errorDiv.id = `${fieldId}-error`;
errorDiv.className = 'error-message';
const field = document.querySelector(`#${fieldId}`);
field.parentNode.insertBefore(errorDiv, field.nextSibling);
return errorDiv;
}
// Form submission with validation
form.addEventListener('submit', (e) => {
e.preventDefault();
if (validateForm()) {
console.log('Form is valid, submitting...');
// Submit form data to server
} else {
console.log('Form has errors');
}
});
Custom Validation with HTML5
html
<input type="email"
pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$"
required
title="Please enter a valid email address">
Javascript
const input = document.querySelector('input[type="email"]');
input.addEventListener('invalid', (e) => {
e.preventDefault();
const validity = e.target.validity;
if (validity.valueMissing) {
showCustomError('This field is required');
} else if (validity.typeMismatch) {
showCustomError('Please enter a valid email');
} else if (validity.patternMismatch) {
showCustomError('Email format is incorrect');
}
});
4. Timers and Intervals ⏱️
JavaScript provides timers to delay actions or repeat them at intervals.
setTimeout and clearTimeout
Javascript
// Execute once after delay
const timeoutId = setTimeout(() => {
console.log('This runs after 2 seconds');
}, 2000);
// Cancel timeout
clearTimeout(timeoutId);
// Practical example: Auto-hide notification
function showNotification(message) {
const notification = document.createElement('div');
notification.className = 'notification';
notification.textContent = message;
document.body.appendChild(notification);
// Auto-hide after 3 seconds
setTimeout(() => {
notification.remove();
}, 3000);
}
// Debouncing user input
let searchTimeout;
const searchInput = document.querySelector('#search');
searchInput.addEventListener('input', (e) => {
clearTimeout(searchTimeout);
searchTimeout = setTimeout(() => {
performSearch(e.target.value);
}, 500); // Wait 500ms after user stops typing
});
function performSearch(query) {
console.log('Searching for:', query);
// Perform actual search
}
setInterval and clearInterval
Javascript
// Execute repeatedly
const intervalId = setInterval(() => {
console.log('This runs every second');
}, 1000);
// Stop interval
clearInterval(intervalId);
// Practical example: Real-time clock
function startClock() {
const clockElement = document.querySelector('#clock');
const updateClock = () => {
const now = new Date();
clockElement.textContent = now.toLocaleTimeString();
};
updateClock(); // Update immediately
return setInterval(updateClock, 1000); // Update every second
}
const clockInterval = startClock();
// Stop clock when needed
// clearInterval(clockInterval);
// Auto-refresh data
function startAutoRefresh() {
return setInterval(() => {
fetchLatestData()
.then(data => updateUI(data))
.catch(error => console.error('Refresh failed:', error));
}, 30000); // Refresh every 30 seconds
}
5. LocalStorage, SessionStorage, and Cookies 📦
Storing data on the client side is essential for personalization and state management.
Local Storage stores data inside your browser, and the data does not get deleted even after the browser is closed.
Session Storage stores data temporarily, meaning the data will be lost as soon as the tab or browser is closed.
Cookies also store data, and your data is saved in the browser’s “cookies” property. Cookies are mostly used for small or lightweight data, such as preferences, user tracking, etc.
Key Concepts:
localStorage API:
localStorage.setItem("name", "Shanu"); console.log(localStorage.getItem("name")); // "Shanu" localStorage.removeItem("name"); localStorage.clear();
sessionStorage: works the same, but clears when the tab closes
Storing JSON: Local Storage only stores strings. If you want to store objects or arrays, convert them using
JSON.stringify()
andJSON.parse()
.localStorage.setItem("user", JSON.stringify({ id: 1, name: "Shanu" })); console.log(JSON.parse(localStorage.getItem("user")));
Working with Cookies: Cookies are sent with HTTP requests and have more complex syntax
Cookies are used to store data, and they are around 4 KB in size, while Local Storage and Session Storage can store up to 5 MB.
Javascript
// Setting cookies (manual approach)
function setCookie(name, value, days = 7) {
const expires = new Date();
expires.setTime(expires.getTime() + (days * 24 * 60 * 60 * 1000));
document.cookie = `${name}=${value};expires=${expires.toUTCString()};path=/`;
}
// Getting cookies
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) {
return parts.pop().split(';').shift();
}
return null;
}
// Deleting cookies
function deleteCookie(name) {
document.cookie = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/;`;
}
// Usage
setCookie('userToken', 'abc123', 30); // Expires in 30 days
const token = getCookie('userToken');
deleteCookie('userToken');
// Practical example: Remember user preferences
function saveUserPreference(key, value) {
const preferences = getCookie('userPrefs');
const prefs = preferences ? JSON.parse(preferences) : {};
prefs[key] = value;
setCookie('userPrefs', JSON.stringify(prefs), 365); // 1 year
}
function getUserPreference(key, defaultValue = null) {
const preferences = getCookie('userPrefs');
if (preferences) {
const prefs = JSON.parse(preferences);
return prefs[key] || defaultValue;
}
return defaultValue;
}
Storage Comparison and Best Practices
Javascript
// Choose the right storage method
class StorageManager {
// For user preferences, app settings
static saveUserData(key, data) {
localStorage.setItem(key, JSON.stringify(data));
}
// For temporary session data
static saveSessionData(key, data) {
sessionStorage.setItem(key, JSON.stringify(data));
}
// For authentication tokens, cross-tab communication
static saveAuthToken(token) {
setCookie('authToken', token, 1); // 1 day
}
// Generic getter with fallback
static getData(key, defaultValue = null) {
// Try localStorage first
let data = localStorage.getItem(key);
if (data) return JSON.parse(data);
// Try sessionStorage
data = sessionStorage.getItem(key);
if (data) return JSON.parse(data);
// Try cookies
data = getCookie(key);
if (data) return JSON.parse(data);
return defaultValue;
}
}
// Usage
StorageManager.saveUserData('preferences', { theme: 'dark', language: 'en' });
const preferences = StorageManager.getData('preferences', { theme: 'light' });
Key Takeaways and Best Practices
DOM Manipulation
Use modern selectors:
querySelector
andquerySelectorAll
over older methodsPrefer classList over style: Use CSS classes for styling changes
Cache DOM references: Don't query the same element repeatedly
Use event delegation: For dynamic content and better performance
Event Handling
Always use addEventListener: Never use inline event handlers in production
Remove event listeners: When elements are removed to prevent memory leaks
Use event delegation: For lists and dynamic content
Understand event phases: Bubbling vs capturing
Forms
Always validate on both client and server: Client validation is for UX, not security
Provide real-time feedback: Don't wait for form submission
Use semantic HTML: Leverage built-in browser validation when possible
Handle edge cases: Empty values, special characters, etc
Storage
localStorage for persistence: User preferences, app settings
sessionStorage for temporary data: Form drafts, wizard state
Cookies for server communication: Authentication, cross-tab data
Always handle JSON carefully: Use try-catch when parsing
Consider storage limits: ~5-10MB for localStorage/sessionStorage
Final Thoughts 💡
By now, you should have a solid understanding of how JavaScript powers interaction with the browser – from modifying the DOM and handling user input, to running timers and storing data.The more you practice DOM manipulation and event handling, the more natural it becomes!
#JavaScript #DOM #WebDevelopment #Frontend #EventHandling #Forms #BrowserAPIs #localStorage #Interactive
Subscribe to my newsletter
Read articles from Shanu Tiwari directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Shanu Tiwari
Shanu Tiwari
I'm Shanu Tiwari, a passionate front-end software engineer. I'm driven by the power of technology to create innovative solutions and improve user experiences. Through my studies and personal projects, I have developed a strong foundation in programming languages such as Javascript and TypeScript, as well as a solid understanding of software development methodologies.