Skip to main content

DOM & Browser APIs

The Document Object Model (DOM) is a tree-like representation of your HTML. JavaScript can read, modify, and react to changes in the DOM. This is what makes web pages interactive.

1. Selecting Elements

Modern Methods

// Single element (first match)
const header = document.querySelector('h1');
const button = document.querySelector('#submit-btn');
const card = document.querySelector('.card');

// Multiple elements (NodeList)
const items = document.querySelectorAll('.item');
const links = document.querySelectorAll('a[href^="https"]');

// Iterate NodeList
items.forEach(item => console.log(item.textContent));

// Convert to array for more methods
const itemsArray = [...items];
const filtered = itemsArray.filter(item => item.classList.contains('active'));

Legacy Methods (Still Useful)

document.getElementById('myId');           // Single element
document.getElementsByClassName('myClass'); // HTMLCollection
document.getElementsByTagName('div');      // HTMLCollection
Best Practice: Use querySelector and querySelectorAll for flexibility. They accept any CSS selector.

2. Traversing the DOM

Navigate relative to an element.
const item = document.querySelector('.item');

// Parents
item.parentElement;             // Direct parent
item.closest('.container');     // Closest ancestor matching selector

// Children
item.children;                  // HTMLCollection of child elements
item.firstElementChild;         // First child element
item.lastElementChild;          // Last child element

// Siblings
item.nextElementSibling;        // Next sibling element
item.previousElementSibling;    // Previous sibling element

3. Modifying Elements

Text & HTML Content

const heading = document.querySelector('h1');

// Text content (safe, no HTML parsing)
heading.textContent = 'New Heading';

// HTML content (parses HTML, use with caution)
heading.innerHTML = '<span>New</span> Heading';

// Outer HTML (includes the element itself)
heading.outerHTML = '<h2>Replaced!</h2>';
Security: Never use innerHTML with user input. It can lead to XSS attacks. Use textContent or sanitize the input.

Attributes

const link = document.querySelector('a');

// Get/Set attributes
link.getAttribute('href');
link.setAttribute('href', 'https://example.com');
link.removeAttribute('target');
link.hasAttribute('rel');

// Direct property access (for standard attributes)
link.href = 'https://example.com';
link.id = 'main-link';

// Data attributes
// <div data-user-id="123" data-role="admin">
const div = document.querySelector('div');
div.dataset.userId;  // '123'
div.dataset.role;    // 'admin'
div.dataset.newProp = 'value'; // Adds data-new-prop="value"

Classes

const element = document.querySelector('.card');

// Modern classList API
element.classList.add('active', 'highlighted');
element.classList.remove('hidden');
element.classList.toggle('collapsed');
element.classList.contains('active');  // true/false
element.classList.replace('old', 'new');

// Multiple operations
element.className = 'card active';  // Overwrites all classes

Styles

const box = document.querySelector('.box');

// Inline styles (not recommended for many changes)
box.style.backgroundColor = 'blue';
box.style.marginTop = '20px';
box.style.cssText = 'color: red; font-size: 16px;';

// Get computed styles
const styles = getComputedStyle(box);
styles.width;  // '200px'

4. Creating & Removing Elements

Creating Elements

// Create element
const div = document.createElement('div');
div.className = 'card';
div.textContent = 'Hello World';

// Create from HTML string
const template = `
    <div class="card">
        <h2>Title</h2>
        <p>Content</p>
    </div>
`;
// Use insertAdjacentHTML (see below)

Inserting Elements

const container = document.querySelector('.container');
const newElement = document.createElement('div');

// Append/Prepend (multiple nodes allowed)
container.append(newElement);           // Add to end
container.prepend(newElement);          // Add to start
container.append('Text', anotherEl);    // Multiple items

// Insert at specific position
container.insertAdjacentElement('beforebegin', newElement); // Before container
container.insertAdjacentElement('afterbegin', newElement);  // First child
container.insertAdjacentElement('beforeend', newElement);   // Last child
container.insertAdjacentElement('afterend', newElement);    // After container

// Insert HTML string
container.insertAdjacentHTML('beforeend', '<div class="new">New</div>');

Removing & Replacing

const element = document.querySelector('.to-remove');

// Remove
element.remove();

// Replace
const newEl = document.createElement('span');
element.replaceWith(newEl);

// Clone
const clone = element.cloneNode(true);  // true = deep clone
container.append(clone);

5. Event Handling

Adding Event Listeners

const button = document.querySelector('button');

// Standard way
button.addEventListener('click', (event) => {
    console.log('Clicked!', event);
});

// With options
button.addEventListener('click', handler, {
    once: true,     // Remove after first trigger
    passive: true,  // Never calls preventDefault (scroll perf)
    capture: true   // Trigger during capture phase
});

// Remove listener
button.removeEventListener('click', handler);

The Event Object

document.addEventListener('click', (event) => {
    event.target;          // Element that triggered the event
    event.currentTarget;   // Element the listener is attached to
    event.type;            // 'click'
    event.timeStamp;       // When it happened
    
    event.preventDefault();  // Stop default behavior (e.g., form submit)
    event.stopPropagation(); // Stop bubbling to parent elements
});

Event Delegation

Instead of adding listeners to many elements, add one to a parent.
// ❌ Bad: Listener on each item
document.querySelectorAll('.item').forEach(item => {
    item.addEventListener('click', handleClick);
});

// ✅ Good: Delegate to parent
document.querySelector('.list').addEventListener('click', (event) => {
    if (event.target.matches('.item')) {
        handleClick(event.target);
    }
    // Or for nested elements:
    const item = event.target.closest('.item');
    if (item) handleClick(item);
});

Common Events

// Mouse
element.addEventListener('click', fn);
element.addEventListener('dblclick', fn);
element.addEventListener('mouseenter', fn);  // No bubble
element.addEventListener('mouseleave', fn);  // No bubble
element.addEventListener('mouseover', fn);   // Bubbles

// Keyboard
document.addEventListener('keydown', (e) => {
    console.log(e.key);     // 'Enter', 'a', 'ArrowUp'
    console.log(e.code);    // 'Enter', 'KeyA', 'ArrowUp'
    console.log(e.ctrlKey); // true if Ctrl held
});

// Form
form.addEventListener('submit', (e) => {
    e.preventDefault();
    const data = new FormData(form);
});
input.addEventListener('input', fn);   // Every change
input.addEventListener('change', fn);  // On blur/enter

// Window
window.addEventListener('load', fn);          // All resources loaded
window.addEventListener('DOMContentLoaded', fn); // DOM ready
window.addEventListener('resize', fn);
window.addEventListener('scroll', fn);

6. Browser APIs

Local Storage

Persist data in the browser (survives page refresh).
// Store
localStorage.setItem('user', JSON.stringify({ name: 'Alice' }));

// Retrieve
const user = JSON.parse(localStorage.getItem('user'));

// Remove
localStorage.removeItem('user');

// Clear all
localStorage.clear();

// Session storage (cleared when tab closes)
sessionStorage.setItem('temp', 'data');

Fetch API

Make HTTP requests (covered in Async chapter).
const response = await fetch('/api/data', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ key: 'value' })
});
const data = await response.json();

Geolocation

navigator.geolocation.getCurrentPosition(
    (position) => {
        console.log(position.coords.latitude);
        console.log(position.coords.longitude);
    },
    (error) => {
        console.error(error.message);
    }
);

Intersection Observer

Efficiently detect when elements enter/exit the viewport.
const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
        if (entry.isIntersecting) {
            entry.target.classList.add('visible');
            observer.unobserve(entry.target); // Stop observing
        }
    });
}, {
    threshold: 0.5  // 50% visible
});

document.querySelectorAll('.animate-on-scroll').forEach(el => {
    observer.observe(el);
});

Clipboard API

// Copy to clipboard
await navigator.clipboard.writeText('Copied text!');

// Read from clipboard
const text = await navigator.clipboard.readText();

History API

Manipulate browser history for SPAs.
// Add to history (without page reload)
history.pushState({ page: 1 }, 'Title', '/new-url');

// Replace current entry
history.replaceState({ page: 2 }, 'Title', '/updated-url');

// Listen for back/forward
window.addEventListener('popstate', (event) => {
    console.log(event.state); // { page: 1 }
});

7. Forms

FormData

const form = document.querySelector('form');

form.addEventListener('submit', (e) => {
    e.preventDefault();
    
    const formData = new FormData(form);
    
    // Get values
    formData.get('username');
    formData.get('email');
    
    // Convert to object
    const data = Object.fromEntries(formData);
    
    // Send to server
    fetch('/api/submit', {
        method: 'POST',
        body: formData  // Automatically sets Content-Type
    });
});

Validation

const input = document.querySelector('input[name="email"]');

// Built-in validation
input.required = true;
input.pattern = '[a-z]+@[a-z]+\\.[a-z]+';

// Check validity
input.checkValidity();      // true/false
input.validity.valid;       // true/false
input.validity.valueMissing; // true if empty but required
input.validity.patternMismatch; // true if doesn't match pattern

// Custom validation
input.setCustomValidity('Please enter a valid email');
input.setCustomValidity(''); // Clear error

Summary

The DOM is your interface to the web page:
  • Selection: Use querySelector and querySelectorAll.
  • Modification: Change text, attributes, classes, and styles.
  • Creation: createElement, append, insertAdjacentHTML.
  • Events: Use delegation for better performance.
  • Browser APIs: Storage, Geolocation, Intersection Observer, etc.
With these fundamentals, you can build dynamic, interactive web applications!

🎉 Course Complete!

Congratulations on completing the JavaScript Crash Course! You now have a solid foundation in:
  1. Fundamentals — Variables, types, and control flow
  2. Functions & Scope — Closures and higher-order functions
  3. Objects & Prototypes — The JavaScript object model
  4. Async JavaScript — Promises and async/await
  5. Modern JavaScript — ES6+ features
  6. DOM & Browser APIs — Building interactive web pages

What’s Next?

  • Practice: Build projects! A todo app, weather app, or portfolio site.
  • Frameworks: Learn React, Vue, or Angular.
  • Backend: Explore Node.js for server-side JavaScript.
  • TypeScript: Add static typing to your JavaScript.
Happy coding! 🚀