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

Shanu TiwariShanu Tiwari
11 min read

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

  1. 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() and JSON.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

  1. Use modern selectors: querySelector and querySelectorAll over older methods

  2. Prefer classList over style: Use CSS classes for styling changes

  3. Cache DOM references: Don't query the same element repeatedly

  4. Use event delegation: For dynamic content and better performance

Event Handling

  1. Always use addEventListener: Never use inline event handlers in production

  2. Remove event listeners: When elements are removed to prevent memory leaks

  3. Use event delegation: For lists and dynamic content

  4. Understand event phases: Bubbling vs capturing

Forms

  1. Always validate on both client and server: Client validation is for UX, not security

  2. Provide real-time feedback: Don't wait for form submission

  3. Use semantic HTML: Leverage built-in browser validation when possible

  4. Handle edge cases: Empty values, special characters, etc

Storage

  1. localStorage for persistence: User preferences, app settings

  2. sessionStorage for temporary data: Form drafts, wizard state

  3. Cookies for server communication: Authentication, cross-tab data

  4. Always handle JSON carefully: Use try-catch when parsing

  5. 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

10
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.