DOM Manipulation

Selecting and updating elements

The DOM (Document Object Model)

The DOM is a tree-like representation of your HTML that JavaScript can read and modify. Every HTML element is a node in this tree.

Selecting Elements

// Select single element
const heading = document.querySelector('h1');
const button = document.querySelector('#submit-btn');
const firstCard = document.querySelector('.card');

// Select multiple elements (returns NodeList)
const allCards = document.querySelectorAll('.card');
const allLinks = document.querySelectorAll('a');

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

// Old methods (still work, but querySelector is preferred)
const byId = document.getElementById('my-id');
const byClass = document.getElementsByClassName('my-class');
const byTag = document.getElementsByTagName('p');

Modifying Content

const element = document.querySelector('#content');

// Change text content (safe, no HTML parsing)
element.textContent = "Hello, World!";

// Change HTML content (be careful with user input!)
element.innerHTML = 'Hello, World!';

// Change specific attribute
const link = document.querySelector('a');
link.href = 'https://example.com';
link.title = 'Visit Example';

// Using setAttribute
link.setAttribute('target', '_blank');
link.setAttribute('rel', 'noopener');

Modifying Styles

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

// Direct style modification (inline styles)
box.style.backgroundColor = 'blue';
box.style.width = '200px';
box.style.padding = '20px';

// Add/remove classes (preferred method)
box.classList.add('active');
box.classList.remove('hidden');
box.classList.toggle('dark-mode');

// Check if class exists
if (box.classList.contains('active')) {
  console.log('Box is active');
}

// Multiple classes
box.classList.add('large', 'rounded', 'shadow');

Creating & Adding Elements

// Create new element
const div = document.createElement('div');
div.textContent = 'Hello';
div.className = 'card';
div.id = 'my-card';

// Add to DOM
const container = document.querySelector('#container');
container.appendChild(div);  // Add to end
container.prepend(div);      // Add to beginning

// Insert adjacent
const reference = document.querySelector('.reference');
reference.insertAdjacentElement('beforebegin', div);  // Before element
reference.insertAdjacentElement('afterbegin', div);   // First child
reference.insertAdjacentElement('beforeend', div);    // Last child
reference.insertAdjacentElement('afterend', div);     // After element

// Insert HTML string
element.insertAdjacentHTML('beforeend', '

New paragraph

');

Removing Elements

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

// Modern way
element.remove();

// Old way (still works)
element.parentNode.removeChild(element);

// Remove all children
const container = document.querySelector('#container');
container.innerHTML = '';  // Quick but not ideal

// Better way to remove all children
while (container.firstChild) {
  container.removeChild(container.firstChild);
}

Traversing the DOM

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

// Parents
element.parentElement;        // Direct parent
element.closest('.container'); // Nearest ancestor matching selector

// Children
element.children;              // All child elements (HTMLCollection)
element.firstElementChild;     // First child
element.lastElementChild;      // Last child

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

Getting & Setting Attributes

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

// Get attribute
const src = img.getAttribute('src');
const alt = img.getAttribute('alt');

// Set attribute
img.setAttribute('src', 'new-image.jpg');
img.setAttribute('alt', 'New image');

// Remove attribute
img.removeAttribute('title');

// Check if attribute exists
if (img.hasAttribute('data-id')) {
  console.log('Has data-id');
}

// Data attributes
img.dataset.userId = '123';  // Sets data-user-id="123"
console.log(img.dataset.userId);  // "123"

Practical Examples

// Example 1: Toggle visibility
const button = document.querySelector('#toggle-btn');
const content = document.querySelector('#content');

button.addEventListener('click', () => {
  content.classList.toggle('hidden');
});

// Example 2: Dynamic list
const list = document.querySelector('#todo-list');
const input = document.querySelector('#todo-input');
const addBtn = document.querySelector('#add-btn');

addBtn.addEventListener('click', () => {
  const text = input.value.trim();
  if (!text) return;
  
  const li = document.createElement('li');
  li.textContent = text;
  li.className = 'todo-item';
  
  list.appendChild(li);
  input.value = '';  // Clear input
});

// Example 3: Form validation
const form = document.querySelector('#signup-form');
const emailInput = document.querySelector('#email');
const error = document.querySelector('#error');

form.addEventListener('submit', (e) => {
  e.preventDefault();
  
  const email = emailInput.value;
  if (!email.includes('@')) {
    error.textContent = 'Invalid email';
    error.classList.remove('hidden');
    emailInput.classList.add('error');
  } else {
    error.classList.add('hidden');
    emailInput.classList.remove('error');
    // Submit form...
  }
});

🎯 Performance Tips

  • • Cache DOM queries (don't query the same element repeatedly)
  • • Use documentFragment for multiple insertions
  • • Batch DOM changes together (causes fewer reflows)
  • • Use event delegation for many similar elements

⚠️ Common Pitfalls

  • • Setting innerHTML with user input (XSS vulnerability!)
  • • Not checking if element exists before manipulating
  • • Querying inside loops (cache outside instead)
  • • Forgetting that querySelectorAll returns a NodeList, not Array

✓ Best Practices

  • • Use querySelector / querySelectorAll for selecting
  • • Prefer textContent over innerHTML when no HTML needed
  • • Use classList methods instead of className manipulation
  • • Cache DOM references in variables
  • • For complex UIs, consider using a framework (React, Vue, etc.)