Progress10 of 20 topics

50% complete

JavaScript DOM Manipulation

The Document Object Model (DOM) is a programming interface for web documents. It represents the page so that programs can change the document structure, style, and content. The DOM represents the document as nodes and objects, allowing JavaScript to interact with the page.

What is the DOM?

Think of the DOM as a tree-like structure where each node is an object representing a part of the document. The DOM provides a way to:

  • Access and modify HTML elements
  • Change element attributes and styles
  • Add or remove elements
  • Respond to user interactions
  • Create dynamic, interactive web pages

Understanding the DOM Tree

When a web page is loaded, the browser creates a DOM of the page. The HTML DOM model is constructed as a tree of objects:

document
  └── html
      ├── head
      │   ├── title
      │   ├── meta
      │   └── link
      └── body
          ├── header
          │   └── h1
          ├── nav
          │   └── ul
          │       ├── li
          │       └── li
          ├── main
          │   ├── h2
          │   └── p
          └── footer
              └── p

Each box in the tree is a node, and each node can have multiple child nodes. The document node is the root node and represents the entire document.

Accessing DOM Elements

JavaScript provides several ways to access elements in the DOM:

JavaScript
1// By ID - returns a single element
2const headerElement = document.getElementById('header');
3
4// By class name - returns a collection of elements
5const paragraphs = document.getElementsByClassName('paragraph');
6
7// By tag name - returns a collection of elements
8const allDivs = document.getElementsByTagName('div');
9
10// By CSS selector - returns the first matching element
11const firstButton = document.querySelector('.btn-primary');
12
13// By CSS selector - returns all matching elements
14const allButtons = document.querySelectorAll('.btn');
15
16// Navigating through the DOM tree
17const parentElement = headerElement.parentNode;
18const firstChild = headerElement.firstChild;
19const nextSibling = headerElement.nextSibling;
20const previousSibling = headerElement.previousSibling;
21const children = headerElement.childNodes;

Modern Selectors

For modern web development, querySelector() and querySelectorAll() are generally preferred over older methods like getElementById() because:

  • They use the same syntax as CSS selectors, which is more consistent
  • They're more flexible and can select elements using complex criteria
  • They work with any valid CSS selector, not just IDs, classes, or tag names

Modifying DOM Elements

Changing Text Content

JavaScript
1// Get the element
2const heading = document.querySelector('h1');
3
4// Change text content
5heading.textContent = 'New Heading Text';
6
7// Change HTML content (be careful with this for security reasons)
8heading.innerHTML = 'New Heading with <span class="highlight">highlighted</span> text';
9
10// innerText vs textContent
11const paragraph = document.querySelector('p');
12console.log(paragraph.innerText); // Shows only visible text
13console.log(paragraph.textContent); // Shows all text, including hidden elements

Changing Attributes

JavaScript
1// Get the element
2const link = document.querySelector('a');
3
4// Get attribute value
5const href = link.getAttribute('href');
6console.log(href); // e.g., "https://example.com"
7
8// Set attribute value
9link.setAttribute('href', 'https://newwebsite.com');
10link.setAttribute('target', '_blank');
11
12// Check if attribute exists
13if (link.hasAttribute('rel')) {
14 console.log('Rel attribute exists');
15}
16
17// Remove attribute
18link.removeAttribute('target');
19
20// Direct property access (for standard attributes)
21link.href = 'https://anotherwebsite.com';
22link.id = 'main-link';

Modifying CSS Styles

JavaScript
1// Get the element
2const box = document.querySelector('.box');
3
4// Inline styles using the style property
5box.style.backgroundColor = 'blue';
6box.style.color = 'white';
7box.style.padding = '20px';
8box.style.borderRadius = '5px';
9
10// Note: CSS properties with hyphens are written in camelCase
11// e.g., 'background-color' becomes 'backgroundColor'
12
13// Getting computed styles (actual styles after CSS is applied)
14const computedStyle = window.getComputedStyle(box);
15console.log(computedStyle.width); // e.g., "200px"
16
17// Adding/removing/toggling CSS classes
18box.classList.add('highlight');
19box.classList.remove('hidden');
20box.classList.toggle('active'); // Adds if absent, removes if present
21box.classList.replace('old-class', 'new-class');
22
23// Checking if an element has a specific class
24if (box.classList.contains('highlight')) {
25 console.log('Box is highlighted');
26}

style vs. classList

style property: Good for changing individual styles dynamically

JavaScript
// Direct style manipulation
element.style.color = 'red';
element.style.fontSize = '20px';

style vs. classList

classList: Better for applying predefined styles from CSS

JavaScript
// Using CSS classes (preferred)
element.classList.add('highlighted');
element.classList.toggle('active');

Creating and Removing Elements

Creating New Elements

JavaScript
1// Create a new element
2const newParagraph = document.createElement('p');
3
4// Add content to the element
5newParagraph.textContent = 'This is a new paragraph.';
6
7// Add attributes
8newParagraph.id = 'new-paragraph';
9newParagraph.classList.add('highlight');
10
11// Create a text node
12const textNode = document.createTextNode('This is just text.');
13
14// Append the new element to an existing element
15const container = document.querySelector('.container');
16container.appendChild(newParagraph);
17
18// Insert before another element
19const existingElement = document.querySelector('.existing');
20container.insertBefore(newParagraph, existingElement);
21
22// Modern insertion methods
23container.append(newParagraph); // Adds at the end, accepts multiple nodes and text
24container.prepend(textNode); // Adds at the beginning
25existingElement.before(newParagraph); // Adds before the element
26existingElement.after(newParagraph); // Adds after the element

Removing Elements

JavaScript
1// Get the element to remove
2const elementToRemove = document.querySelector('.to-remove');
3
4// Remove the element
5elementToRemove.remove(); // Modern way
6
7// Traditional way (using the parent)
8const parent = elementToRemove.parentNode;
9parent.removeChild(elementToRemove);
10
11// Removing all children
12while (container.firstChild) {
13 container.removeChild(container.firstChild);
14}
15
16// Modern way to remove all children
17container.innerHTML = ''; // Simple but less efficient for large DOMs

Cloning Elements

JavaScript
1// Get the element to clone
2const original = document.querySelector('.template');
3
4// Clone the element (false = don't include children, true = include children)
5const clone = original.cloneNode(true);
6
7// Modify the clone
8clone.id = 'cloned-element';
9clone.classList.add('clone');
10
11// Add the clone to the document
12document.body.appendChild(clone);

Traversing the DOM

Navigating through the DOM tree is a common task when working with web pages:

JavaScript
1// Get an element
2const list = document.querySelector('ul');
3
4// Parent relationships
5const parent = list.parentNode; // or parentElement
6const grandparent = list.parentNode.parentNode;
7
8// Child relationships
9const firstChild = list.firstChild; // Might be a text node (whitespace)
10const firstElement = list.firstElementChild; // First actual element
11const children = list.childNodes; // All child nodes (including text nodes)
12const elementChildren = list.children; // Only element nodes
13const lastChild = list.lastChild;
14const lastElement = list.lastElementChild;
15const childCount = list.childElementCount;
16
17// Sibling relationships
18const nextSibling = list.nextSibling; // Might be a text node
19const nextElement = list.nextElementSibling; // Next element sibling
20const prevSibling = list.previousSibling;
21const prevElement = list.previousElementSibling;
22
23// Example: Iterating through child elements
24for (let i = 0; i < list.children.length; i++) {
25 console.log(list.children[i].textContent);
26}
27
28// Modern way with Array.from()
29Array.from(list.children).forEach(item => {
30 console.log(item.textContent);
31});

Working with Forms

JavaScript can be used to access, validate, and manipulate form data:

JavaScript
1// Get a form
2const form = document.querySelector('#registration-form');
3
4// Access form elements by name
5const username = form.elements.username; // or form.elements['username']
6const email = form.elements.email;
7
8// Get and set values
9console.log(username.value); // Get current value
10username.value = 'JohnDoe'; // Set new value
11
12// Check if a checkbox or radio button is checked
13const agreeCheckbox = form.elements.agree;
14if (agreeCheckbox.checked) {
15 console.log('User agreed to terms');
16}
17
18// Focus on an input
19username.focus();
20
21// Blur (unfocus) an input
22username.blur();
23
24// Select all text in an input
25username.select();
26
27// Form validation
28username.required = true; // Make field required
29email.pattern = '[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$'; // Email regex pattern
30
31// Form submission
32form.addEventListener('submit', function(event) {
33 // Prevent the default form submission
34 event.preventDefault();
35
36 // Validate form
37 if (username.value.length < 3) {
38 alert('Username must be at least 3 characters long');
39 return;
40 }
41
42 // Submit form programmatically if validation passes
43 console.log('Form submitted!');
44 // form.submit(); // Uncomment to actually submit
45});

DOM Manipulation Best Practices

IssueBad PracticeGood Practice
Multiple DOM UpdatesUpdating the DOM in a loopBatch updates using DocumentFragment
Reflows and RepaintsReading layout properties between style changesGroup all reads, then do all writes
Event HandlingAdding event listeners to many elementsUse event delegation
Content InsertionUsing innerHTML for simple textUse textContent for text-only changes
Selector PerformanceUsing complex selectors repeatedlyCache DOM references in variables

Using DocumentFragment for Batch Updates

JavaScript
1// Bad: Adding multiple elements directly to the DOM
2const list = document.querySelector('#list');
3
4for (let i = 0; i < 100; i++) {
5 const item = document.createElement('li');
6 item.textContent = `Item ${i}`;
7 list.appendChild(item); // Causes reflow each time
8}
9
10// Good: Using DocumentFragment for batch updates
11const list = document.querySelector('#list');
12const fragment = document.createDocumentFragment();
13
14for (let i = 0; i < 100; i++) {
15 const item = document.createElement('li');
16 item.textContent = `Item ${i}`;
17 fragment.appendChild(item); // No reflow
18}
19
20list.appendChild(fragment); // Only one reflow

Event Delegation

JavaScript
1// Bad: Adding event listeners to each button
2const buttons = document.querySelectorAll('.btn');
3buttons.forEach(button => {
4 button.addEventListener('click', function() {
5 console.log('Button clicked:', this.textContent);
6 });
7});
8
9// Good: Using event delegation
10const container = document.querySelector('.buttons-container');
11container.addEventListener('click', function(event) {
12 if (event.target.classList.contains('btn')) {
13 console.log('Button clicked:', event.target.textContent);
14 }
15});

Practical Example: Dynamic List

Let's create a simple to-do list application to demonstrate DOM manipulation:

JavaScript
1// HTML structure (for reference)
2/*
3<div class="todo-app">
4 <h2 className='text-xl md:text-3xl font-bold mb-4'>To-Do List</h2>
5 <form id="todo-form">
6 <input type="text" id="todo-input" placeholder="Add new task...">
7 <button type="submit">Add</button>
8 </form>
9 <ul id="todo-list"></ul>
10</div>
11*/
12
13// JavaScript
14document.addEventListener('DOMContentLoaded', function() {
15 const todoForm = document.getElementById('todo-form');
16 const todoInput = document.getElementById('todo-input');
17 const todoList = document.getElementById('todo-list');
18
19 // Add new task
20 todoForm.addEventListener('submit', function(event) {
21 event.preventDefault();
22
23 // Get input value and trim whitespace
24 const taskText = todoInput.value.trim();
25
26 // Check if input is not empty
27 if (taskText !== '') {
28 // Create new list item
29 const listItem = document.createElement('li');
30 listItem.className = 'todo-item';
31
32 // Create task text span
33 const taskSpan = document.createElement('span');
34 taskSpan.className = 'task-text';
35 taskSpan.textContent = taskText;
36
37 // Create delete button
38 const deleteBtn = document.createElement('button');
39 deleteBtn.className = 'delete-btn';
40 deleteBtn.textContent = 'Delete';
41
42 // Add event listener to toggle completed state
43 taskSpan.addEventListener('click', function() {
44 this.classList.toggle('completed');
45 });
46
47 // Add event listener to delete button
48 deleteBtn.addEventListener('click', function() {
49 listItem.remove();
50 });
51
52 // Append elements to list item
53 listItem.appendChild(taskSpan);
54 listItem.appendChild(deleteBtn);
55
56 // Append list item to todo list
57 todoList.appendChild(listItem);
58
59 // Clear input field
60 todoInput.value = '';
61
62 // Focus on input for next entry
63 todoInput.focus();
64 }
65 });
66
67 // Using event delegation for the entire list
68 todoList.addEventListener('click', function(event) {
69 // Check if clicked element is a delete button
70 if (event.target.classList.contains('delete-btn')) {
71 // Get the parent li element and remove it
72 event.target.parentElement.remove();
73 }
74
75 // Check if clicked element is a task text
76 if (event.target.classList.contains('task-text')) {
77 // Toggle completed class
78 event.target.classList.toggle('completed');
79 }
80 });
81});

Practice Exercises

Try these exercises to reinforce your understanding of DOM manipulation:

JavaScript
// 1. Create a simple image gallery that displays thumbnails and shows
// the selected image in a larger view when clicked.
// 2. Build a form with validation that checks input fields in real-time
// and displays error messages next to invalid fields.
// 3. Create an accordion component that expands/collapses content sections
// when their headers are clicked.
// 4. Build a dynamic table that allows sorting when column headers are clicked
// and filtering based on user input.
// 5. Create a "dark mode" toggle that changes the page's color scheme and
// saves the user's preference to localStorage.

Summary

In this tutorial, you've learned:

  • What the DOM is and how it represents web documents
  • How to select and access elements using various methods
  • How to modify element content, attributes, and styles
  • How to create, clone, and remove elements
  • How to traverse the DOM tree
  • How to work with forms and form elements
  • Best practices for efficient DOM manipulation
  • How to build a practical application using DOM manipulation

DOM manipulation is fundamental to creating dynamic web pages and applications. With these skills, you can transform static HTML into interactive experiences that respond to user actions and update content dynamically. In the next tutorial, we'll explore JavaScript events in more detail to make your web pages even more interactive.

Related Tutorials

Learn how to handle user interactions and browser events.

Learn more

Learn about specialized data structures in JavaScript.

Learn more

Learn about promises, async/await, and handling asynchronous operations.

Learn more