🌳 DOM Manipulation

Interacting with Web Pages

The Document Object Model

The DOM is a programming interface for HTML documents. It represents the page structure as a tree of objects that JavaScript can manipulate.

🔍 Selecting Elements

getElementById

// Select by ID (returns single element or null)
let header = document.getElementById('header');
console.log(header);

// Common usage
let button = document.getElementById('submitBtn');
if (button) {
    button.textContent = 'Click Me';
}

getElementsByClassName

// Select by class name (returns HTMLCollection)
let cards = document.getElementsByClassName('card');
console.log(cards.length);

// Convert to array for array methods
let cardsArray = Array.from(cards);
cardsArray.forEach(card => {
    console.log(card.textContent);
});

// Live collection (updates automatically)
console.log(cards.length);  // e.g., 3
let newCard = document.createElement('div');
newCard.className = 'card';
document.body.appendChild(newCard);
console.log(cards.length);  // 4 (updated automatically)

getElementsByTagName

// Select by tag name (returns HTMLCollection)
let paragraphs = document.getElementsByTagName('p');
let divs = document.getElementsByTagName('div');

// Get all elements
let allElements = document.getElementsByTagName('*');

querySelector and querySelectorAll

// querySelector - returns first match (CSS selectors)
let header = document.querySelector('#header');
let firstCard = document.querySelector('.card');
let firstButton = document.querySelector('button');
let complexSelect = document.querySelector('div.card > h2');

// querySelectorAll - returns NodeList of all matches
let allCards = document.querySelectorAll('.card');
let allButtons = document.querySelectorAll('button');
let activeItems = document.querySelectorAll('.item.active');

// NodeList can use forEach directly
allCards.forEach(card => {
    console.log(card.textContent);
});

// Advanced selectors
let secondCard = document.querySelector('.card:nth-child(2)');
let notFirst = document.querySelectorAll('.item:not(:first-child)');
let inputs = document.querySelectorAll('input[type="text"]');
let checkedBoxes = document.querySelectorAll('input:checked');

// Selecting within an element
let container = document.querySelector('.container');
let containerButtons = container.querySelectorAll('button');

Modern Shortcuts

// closest - find nearest ancestor matching selector
let button = document.querySelector('.btn');
let card = button.closest('.card');

// matches - check if element matches selector
if (button.matches('.btn-primary')) {
    console.log('Primary button');
}

// contains - check if element is descendant
let container = document.querySelector('.container');
let button = document.querySelector('.btn');
console.log(container.contains(button));  // true/false

✏️ Modifying Content

textContent and innerHTML

let div = document.querySelector('.content');

// textContent - plain text only
console.log(div.textContent);
div.textContent = 'New text';  // Replaces all content

// innerHTML - HTML string
console.log(div.innerHTML);
div.innerHTML = '

Title

Paragraph

'; // innerText - similar to textContent but respects CSS console.log(div.innerText); // Excludes hidden elements // Security warning: innerHTML is vulnerable to XSS // Avoid: div.innerHTML = userInput; // Use: div.textContent = userInput;

Attributes

let img = document.querySelector('img');

// getAttribute and setAttribute
let src = img.getAttribute('src');
img.setAttribute('alt', 'Description');
img.setAttribute('data-id', '123');

// Direct property access (for common attributes)
console.log(img.src);
console.log(img.alt);
img.src = 'new-image.jpg';

// hasAttribute and removeAttribute
if (img.hasAttribute('alt')) {
    img.removeAttribute('alt');
}

// Data attributes
let element = document.querySelector('.item');
element.setAttribute('data-user-id', '42');
element.setAttribute('data-role', 'admin');

// Access via dataset
console.log(element.dataset.userId);   // '42'
console.log(element.dataset.role);     // 'admin'
element.dataset.status = 'active';     // Sets data-status="active"

Classes

let div = document.querySelector('.box');

// className - string of all classes
console.log(div.className);  // 'box active large'
div.className = 'box';       // Replaces all classes

// classList - better API (ES5)
div.classList.add('active');           // Add class
div.classList.remove('inactive');      // Remove class
div.classList.toggle('visible');       // Toggle class
div.classList.contains('active');      // Check if has class

// Multiple classes
div.classList.add('large', 'blue');
div.classList.remove('small', 'red');

// Replace class
div.classList.replace('old-class', 'new-class');

// Conditional toggle
div.classList.toggle('active', isActive);  // Add if isActive true

Styles

let div = document.querySelector('.box');

// Inline styles (camelCase properties)
div.style.color = 'blue';
div.style.backgroundColor = 'yellow';
div.style.fontSize = '20px';
div.style.marginTop = '10px';

// Read computed style (includes CSS from stylesheets)
let computedStyle = window.getComputedStyle(div);
console.log(computedStyle.color);
console.log(computedStyle.fontSize);

// Set multiple styles
Object.assign(div.style, {
    width: '200px',
    height: '100px',
    border: '1px solid black'
});

// Remove inline style
div.style.color = '';  // Remove color
div.removeAttribute('style');  // Remove all inline styles

// CSS custom properties
div.style.setProperty('--main-color', 'blue');
let value = div.style.getPropertyValue('--main-color');

🏗️ Creating and Removing Elements

Creating Elements

// createElement
let div = document.createElement('div');
div.textContent = 'Hello World';
div.className = 'box';
div.id = 'myBox';

// createTextNode
let text = document.createTextNode('Some text');

// Clone existing element
let original = document.querySelector('.card');
let clone = original.cloneNode(true);  // true = deep clone (with children)

// Template strings (create HTML structure)
let html = `
    

Title

Content

`; // Create from string (be careful with user input!) let temp = document.createElement('div'); temp.innerHTML = html; let card = temp.firstElementChild;

Inserting Elements

let container = document.querySelector('.container');
let newDiv = document.createElement('div');

// appendChild - add as last child
container.appendChild(newDiv);

// insertBefore - insert before reference element
let reference = document.querySelector('.existing');
container.insertBefore(newDiv, reference);

// Modern methods (ES2016)
// before - insert before element as sibling
reference.before(newDiv);

// after - insert after element as sibling
reference.after(newDiv);

// prepend - insert as first child
container.prepend(newDiv);

// append - insert as last child (can append multiple)
container.append(newDiv, 'text', anotherElement);

// insertAdjacentElement
reference.insertAdjacentElement('beforebegin', newDiv);  // Before element
reference.insertAdjacentElement('afterbegin', newDiv);   // First child
reference.insertAdjacentElement('beforeend', newDiv);    // Last child
reference.insertAdjacentElement('afterend', newDiv);     // After element

// insertAdjacentHTML - insert HTML string
reference.insertAdjacentHTML('beforeend', '

New paragraph

');

Removing Elements

let element = document.querySelector('.item');

// Modern: remove() method
element.remove();

// Old way: removeChild
let parent = element.parentElement;
parent.removeChild(element);

// Remove all children
let container = document.querySelector('.container');
container.innerHTML = '';  // Fast but loses event listeners

// Or loop through children
while (container.firstChild) {
    container.removeChild(container.firstChild);
}

// replaceChild - replace element
let newElement = document.createElement('div');
parent.replaceChild(newElement, element);

// Modern: replaceWith
element.replaceWith(newElement);

🗂️ Traversing the DOM

let element = document.querySelector('.item');

// Parent
console.log(element.parentElement);     // Parent element
console.log(element.parentNode);        // Parent node (can be non-element)

// Children
console.log(element.children);          // HTMLCollection of child elements
console.log(element.childNodes);        // NodeList (includes text nodes)
console.log(element.firstElementChild); // First child element
console.log(element.lastElementChild);  // Last child element
console.log(element.childElementCount); // Number of children

// Siblings
console.log(element.previousElementSibling);  // Previous sibling
console.log(element.nextElementSibling);      // Next sibling

// Closest ancestor matching selector
let card = element.closest('.card');

// All matches within element
let buttons = element.querySelectorAll('button');

📊 Element Properties

let element = document.querySelector('.box');

// Dimensions (includes padding, excludes border/margin)
console.log(element.clientWidth);
console.log(element.clientHeight);

// Dimensions (includes padding and border)
console.log(element.offsetWidth);
console.log(element.offsetHeight);

// Scroll dimensions
console.log(element.scrollWidth);   // Total width (including overflow)
console.log(element.scrollHeight);  // Total height (including overflow)
console.log(element.scrollTop);     // Pixels scrolled from top
console.log(element.scrollLeft);    // Pixels scrolled from left

// Position relative to offsetParent
console.log(element.offsetTop);
console.log(element.offsetLeft);
console.log(element.offsetParent);

// Bounding rectangle (relative to viewport)
let rect = element.getBoundingClientRect();
console.log(rect.top);
console.log(rect.right);
console.log(rect.bottom);
console.log(rect.left);
console.log(rect.width);
console.log(rect.height);

// Scroll element into view
element.scrollIntoView({ behavior: 'smooth', block: 'center' });

💡 Practical Examples

Create Card Component

function createCard(title, content, imageUrl) {
    let card = document.createElement('div');
    card.className = 'card';
    
    card.innerHTML = `
        ${title}
        

${title}

${content}

`; return card; } // Usage let container = document.querySelector('.cards-container'); let card = createCard('Title', 'Content text', 'image.jpg'); container.appendChild(card);

Toggle Visibility

function toggleElement(selector) {
    let element = document.querySelector(selector);
    if (element) {
        element.classList.toggle('hidden');
        // Or: element.style.display = element.style.display === 'none' ? 'block' : 'none';
    }
}

// Usage
toggleElement('.modal');
toggleElement('#sidebar');

Form Validation

function validateForm(formSelector) {
    let form = document.querySelector(formSelector);
    let inputs = form.querySelectorAll('input[required]');
    let errors = [];
    
    inputs.forEach(input => {
        let errorDiv = input.nextElementSibling;
        
        if (!input.value.trim()) {
            input.classList.add('error');
            if (errorDiv && errorDiv.classList.contains('error-message')) {
                errorDiv.textContent = `${input.name} is required`;
            }
            errors.push(input.name);
        } else {
            input.classList.remove('error');
            if (errorDiv && errorDiv.classList.contains('error-message')) {
                errorDiv.textContent = '';
            }
        }
    });
    
    return errors.length === 0;
}

// Usage
if (validateForm('#contactForm')) {
    console.log('Form is valid');
}

Dynamic List

function renderList(items, containerSelector) {
    let container = document.querySelector(containerSelector);
    container.innerHTML = '';  // Clear existing
    
    let ul = document.createElement('ul');
    
    items.forEach(item => {
        let li = document.createElement('li');
        li.textContent = item;
        li.classList.add('list-item');
        ul.appendChild(li);
    });
    
    container.appendChild(ul);
}

// Usage
let fruits = ['Apple', 'Banana', 'Orange'];
renderList(fruits, '#fruit-list');

Accordion Component

function createAccordion(items) {
    let accordion = document.createElement('div');
    accordion.className = 'accordion';
    
    items.forEach((item, index) => {
        let section = document.createElement('div');
        section.className = 'accordion-item';
        
        let header = document.createElement('div');
        header.className = 'accordion-header';
        header.textContent = item.title;
        header.dataset.index = index;
        
        let content = document.createElement('div');
        content.className = 'accordion-content';
        content.textContent = item.content;
        content.style.display = 'none';
        
        header.addEventListener('click', () => {
            let isOpen = content.style.display === 'block';
            
            // Close all
            accordion.querySelectorAll('.accordion-content').forEach(c => {
                c.style.display = 'none';
            });
            
            // Toggle clicked
            if (!isOpen) {
                content.style.display = 'block';
            }
        });
        
        section.appendChild(header);
        section.appendChild(content);
        accordion.appendChild(section);
    });
    
    return accordion;
}

// Usage
let items = [
    { title: 'Section 1', content: 'Content 1' },
    { title: 'Section 2', content: 'Content 2' }
];
document.body.appendChild(createAccordion(items));

Lazy Loading Images

function lazyLoadImages() {
    let images = document.querySelectorAll('img[data-src]');
    
    let observer = new IntersectionObserver((entries) => {
        entries.forEach(entry => {
            if (entry.isIntersecting) {
                let img = entry.target;
                img.src = img.dataset.src;
                img.removeAttribute('data-src');
                observer.unobserve(img);
            }
        });
    });
    
    images.forEach(img => observer.observe(img));
}

// Usage
// HTML: Lazy loaded
lazyLoadImages();

🎯 Key Takeaways