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:
1// By ID - returns a single element2const headerElement = document.getElementById('header');34// By class name - returns a collection of elements5const paragraphs = document.getElementsByClassName('paragraph');67// By tag name - returns a collection of elements8const allDivs = document.getElementsByTagName('div');910// By CSS selector - returns the first matching element11const firstButton = document.querySelector('.btn-primary');1213// By CSS selector - returns all matching elements14const allButtons = document.querySelectorAll('.btn');1516// Navigating through the DOM tree17const 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
1// Get the element2const heading = document.querySelector('h1');34// Change text content5heading.textContent = 'New Heading Text';67// Change HTML content (be careful with this for security reasons)8heading.innerHTML = 'New Heading with <span class="highlight">highlighted</span> text';910// innerText vs textContent11const paragraph = document.querySelector('p');12console.log(paragraph.innerText); // Shows only visible text13console.log(paragraph.textContent); // Shows all text, including hidden elements
Changing Attributes
1// Get the element2const link = document.querySelector('a');34// Get attribute value5const href = link.getAttribute('href');6console.log(href); // e.g., "https://example.com"78// Set attribute value9link.setAttribute('href', 'https://newwebsite.com');10link.setAttribute('target', '_blank');1112// Check if attribute exists13if (link.hasAttribute('rel')) {14 console.log('Rel attribute exists');15}1617// Remove attribute18link.removeAttribute('target');1920// Direct property access (for standard attributes)21link.href = 'https://anotherwebsite.com';22link.id = 'main-link';
Modifying CSS Styles
1// Get the element2const box = document.querySelector('.box');34// Inline styles using the style property5box.style.backgroundColor = 'blue';6box.style.color = 'white';7box.style.padding = '20px';8box.style.borderRadius = '5px';910// Note: CSS properties with hyphens are written in camelCase11// e.g., 'background-color' becomes 'backgroundColor'1213// Getting computed styles (actual styles after CSS is applied)14const computedStyle = window.getComputedStyle(box);15console.log(computedStyle.width); // e.g., "200px"1617// Adding/removing/toggling CSS classes18box.classList.add('highlight');19box.classList.remove('hidden');20box.classList.toggle('active'); // Adds if absent, removes if present21box.classList.replace('old-class', 'new-class');2223// Checking if an element has a specific class24if (box.classList.contains('highlight')) {25 console.log('Box is highlighted');26}
style vs. classList
style property: Good for changing individual styles dynamically
// Direct style manipulationelement.style.color = 'red';element.style.fontSize = '20px';
style vs. classList
classList: Better for applying predefined styles from CSS
// Using CSS classes (preferred)element.classList.add('highlighted');element.classList.toggle('active');
Creating and Removing Elements
Creating New Elements
1// Create a new element2const newParagraph = document.createElement('p');34// Add content to the element5newParagraph.textContent = 'This is a new paragraph.';67// Add attributes8newParagraph.id = 'new-paragraph';9newParagraph.classList.add('highlight');1011// Create a text node12const textNode = document.createTextNode('This is just text.');1314// Append the new element to an existing element15const container = document.querySelector('.container');16container.appendChild(newParagraph);1718// Insert before another element19const existingElement = document.querySelector('.existing');20container.insertBefore(newParagraph, existingElement);2122// Modern insertion methods23container.append(newParagraph); // Adds at the end, accepts multiple nodes and text24container.prepend(textNode); // Adds at the beginning25existingElement.before(newParagraph); // Adds before the element26existingElement.after(newParagraph); // Adds after the element
Removing Elements
1// Get the element to remove2const elementToRemove = document.querySelector('.to-remove');34// Remove the element5elementToRemove.remove(); // Modern way67// Traditional way (using the parent)8const parent = elementToRemove.parentNode;9parent.removeChild(elementToRemove);1011// Removing all children12while (container.firstChild) {13 container.removeChild(container.firstChild);14}1516// Modern way to remove all children17container.innerHTML = ''; // Simple but less efficient for large DOMs
Cloning Elements
1// Get the element to clone2const original = document.querySelector('.template');34// Clone the element (false = don't include children, true = include children)5const clone = original.cloneNode(true);67// Modify the clone8clone.id = 'cloned-element';9clone.classList.add('clone');1011// Add the clone to the document12document.body.appendChild(clone);
Traversing the DOM
Navigating through the DOM tree is a common task when working with web pages:
1// Get an element2const list = document.querySelector('ul');34// Parent relationships5const parent = list.parentNode; // or parentElement6const grandparent = list.parentNode.parentNode;78// Child relationships9const firstChild = list.firstChild; // Might be a text node (whitespace)10const firstElement = list.firstElementChild; // First actual element11const children = list.childNodes; // All child nodes (including text nodes)12const elementChildren = list.children; // Only element nodes13const lastChild = list.lastChild;14const lastElement = list.lastElementChild;15const childCount = list.childElementCount;1617// Sibling relationships18const nextSibling = list.nextSibling; // Might be a text node19const nextElement = list.nextElementSibling; // Next element sibling20const prevSibling = list.previousSibling;21const prevElement = list.previousElementSibling;2223// Example: Iterating through child elements24for (let i = 0; i < list.children.length; i++) {25 console.log(list.children[i].textContent);26}2728// 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:
1// Get a form2const form = document.querySelector('#registration-form');34// Access form elements by name5const username = form.elements.username; // or form.elements['username']6const email = form.elements.email;78// Get and set values9console.log(username.value); // Get current value10username.value = 'JohnDoe'; // Set new value1112// Check if a checkbox or radio button is checked13const agreeCheckbox = form.elements.agree;14if (agreeCheckbox.checked) {15 console.log('User agreed to terms');16}1718// Focus on an input19username.focus();2021// Blur (unfocus) an input22username.blur();2324// Select all text in an input25username.select();2627// Form validation28username.required = true; // Make field required29email.pattern = '[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$'; // Email regex pattern3031// Form submission32form.addEventListener('submit', function(event) {33 // Prevent the default form submission34 event.preventDefault();3536 // Validate form37 if (username.value.length < 3) {38 alert('Username must be at least 3 characters long');39 return;40 }4142 // Submit form programmatically if validation passes43 console.log('Form submitted!');44 // form.submit(); // Uncomment to actually submit45});
DOM Manipulation Best Practices
Issue | Bad Practice | Good Practice |
---|---|---|
Multiple DOM Updates | Updating the DOM in a loop | Batch updates using DocumentFragment |
Reflows and Repaints | Reading layout properties between style changes | Group all reads, then do all writes |
Event Handling | Adding event listeners to many elements | Use event delegation |
Content Insertion | Using innerHTML for simple text | Use textContent for text-only changes |
Selector Performance | Using complex selectors repeatedly | Cache DOM references in variables |
Using DocumentFragment for Batch Updates
1// Bad: Adding multiple elements directly to the DOM2const list = document.querySelector('#list');34for (let i = 0; i < 100; i++) {5 const item = document.createElement('li');6 item.textContent = `Item ${i}`;7 list.appendChild(item); // Causes reflow each time8}910// Good: Using DocumentFragment for batch updates11const list = document.querySelector('#list');12const fragment = document.createDocumentFragment();1314for (let i = 0; i < 100; i++) {15 const item = document.createElement('li');16 item.textContent = `Item ${i}`;17 fragment.appendChild(item); // No reflow18}1920list.appendChild(fragment); // Only one reflow
Event Delegation
1// Bad: Adding event listeners to each button2const buttons = document.querySelectorAll('.btn');3buttons.forEach(button => {4 button.addEventListener('click', function() {5 console.log('Button clicked:', this.textContent);6 });7});89// Good: Using event delegation10const 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:
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*/1213// JavaScript14document.addEventListener('DOMContentLoaded', function() {15 const todoForm = document.getElementById('todo-form');16 const todoInput = document.getElementById('todo-input');17 const todoList = document.getElementById('todo-list');1819 // Add new task20 todoForm.addEventListener('submit', function(event) {21 event.preventDefault();2223 // Get input value and trim whitespace24 const taskText = todoInput.value.trim();2526 // Check if input is not empty27 if (taskText !== '') {28 // Create new list item29 const listItem = document.createElement('li');30 listItem.className = 'todo-item';3132 // Create task text span33 const taskSpan = document.createElement('span');34 taskSpan.className = 'task-text';35 taskSpan.textContent = taskText;3637 // Create delete button38 const deleteBtn = document.createElement('button');39 deleteBtn.className = 'delete-btn';40 deleteBtn.textContent = 'Delete';4142 // Add event listener to toggle completed state43 taskSpan.addEventListener('click', function() {44 this.classList.toggle('completed');45 });4647 // Add event listener to delete button48 deleteBtn.addEventListener('click', function() {49 listItem.remove();50 });5152 // Append elements to list item53 listItem.appendChild(taskSpan);54 listItem.appendChild(deleteBtn);5556 // Append list item to todo list57 todoList.appendChild(listItem);5859 // Clear input field60 todoInput.value = '';6162 // Focus on input for next entry63 todoInput.focus();64 }65 });6667 // Using event delegation for the entire list68 todoList.addEventListener('click', function(event) {69 // Check if clicked element is a delete button70 if (event.target.classList.contains('delete-btn')) {71 // Get the parent li element and remove it72 event.target.parentElement.remove();73 }7475 // Check if clicked element is a task text76 if (event.target.classList.contains('task-text')) {77 // Toggle completed class78 event.target.classList.toggle('completed');79 }80 });81});
Practice Exercises
Try these exercises to reinforce your understanding of DOM manipulation:
// 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 moreLearn about specialized data structures in JavaScript.
Learn moreLearn about promises, async/await, and handling asynchronous operations.
Learn more