DOM Manipulation
Topic: DOM Manipulation
Concepts Covered: Selecting, modifying, creating, removing elements
What is the DOM?
Section titled “What is the DOM?”The Document Object Model (DOM) is JavaScript’s way of representing and interacting with HTML elements on a webpage. Think of it as a bridge between your JavaScript code and the HTML structure.
Key DOM Concepts
Section titled “Key DOM Concepts”- Every HTML tag becomes an object in JavaScript
- Nested tags are “children” of their parent elements
- The entire document is represented as a tree structure
- JavaScript can modify, add, or remove elements dynamically
DOM Tree Structure
Section titled “DOM Tree Structure”<!DOCTYPE html><html> <!-- document.documentElement --> <head> <!-- document.head --> <title>My Page</title> </head> <body> <!-- document.body --> <h1>Welcome</h1> <p>Hello World</p> </body></html>Selecting Elements
Section titled “Selecting Elements”Before you can modify elements, you need to select them from the DOM.
Essential Selection Methods
Section titled “Essential Selection Methods”querySelector() - Select Single Element
Section titled “querySelector() - Select Single Element”// Select by tag nameconst heading = document.querySelector("h1");
// Select by class (first match)const mainContent = document.querySelector(".main-content");
// Select by IDconst loginForm = document.querySelector("#login-form");
// Select by attributeconst emailInput = document.querySelector('input[type="email"]');
// Complex selectorsconst firstListItem = document.querySelector("ul li:first-child");querySelectorAll() - Select Multiple Elements
Section titled “querySelectorAll() - Select Multiple Elements”// Select all paragraphsconst allParagraphs = document.querySelectorAll("p");
// Select all elements with a specific classconst cards = document.querySelectorAll(".card");
// Select all input elementsconst inputs = document.querySelectorAll("input");
// querySelectorAll returns a NodeList (array-like)console.log(allParagraphs.length); // Number of paragraphsLegacy Selection Methods (Still Useful)
Section titled “Legacy Selection Methods (Still Useful)”// Select by ID (faster than querySelector for IDs)const element = document.getElementById("my-element");
// Select by class nameconst elements = document.getElementsByClassName("my-class");
// Select by tag nameconst paragraphs = document.getElementsByTagName("p");Working with NodeLists
Section titled “Working with NodeLists”const listItems = document.querySelectorAll("li");
// Loop through NodeListlistItems.forEach(function (item, index) { console.log(`Item ${index}: ${item.textContent}`);});
// Convert to array for more methodsconst itemsArray = Array.from(listItems);const itemTexts = itemsArray.map((item) => item.textContent);Modifying Elements
Section titled “Modifying Elements”Once you’ve selected elements, you can change their content, style, and attributes.
Changing Text Content
Section titled “Changing Text Content”const heading = document.querySelector("h1");
// textContent - plain text onlyheading.textContent = "New Heading Text";
// innerHTML - can include HTML tagsconst container = document.querySelector(".container");container.innerHTML = "<p>New paragraph with <strong>bold text</strong></p>";
// Be careful with innerHTML and user input (XSS vulnerability)Modifying Styles
Section titled “Modifying Styles”const element = document.querySelector(".my-element");
// Individual style propertieselement.style.color = "blue";element.style.backgroundColor = "yellow";element.style.fontSize = "20px";element.style.display = "none"; // Hide element
// Multiple styles at onceelement.style.cssText = "color: red; font-size: 18px; margin: 10px;";Working with CSS Classes
Section titled “Working with CSS Classes”const element = document.querySelector(".my-element");
// Add classelement.classList.add("active");element.classList.add("highlight", "visible"); // Add multiple
// Remove classelement.classList.remove("inactive");
// Toggle class (add if not present, remove if present)element.classList.toggle("hidden");
// Check if class existsif (element.classList.contains("active")) { console.log("Element is active");}
// Replace classelement.classList.replace("old-class", "new-class");Modifying Attributes
Section titled “Modifying Attributes”const image = document.querySelector("img");const link = document.querySelector("a");
// Get attributeconst src = image.getAttribute("src");const href = link.getAttribute("href");
// Set attributeimage.setAttribute("src", "new-image.jpg");image.setAttribute("alt", "New image description");link.setAttribute("href", "https://example.com");
// Remove attributeimage.removeAttribute("title");
// Check if attribute existsif (image.hasAttribute("data-id")) { console.log("Image has data-id attribute");}
// Direct property access (for common attributes)image.src = "another-image.jpg";link.href = "https://google.com";Creating and Removing Elements
Section titled “Creating and Removing Elements”Creating New Elements
Section titled “Creating New Elements”<h1>Welcome</h1><p>Hello World</p>p { background-color: #f8f0fc; border: 4px solid #862e9c; padding: 10px; font-size: 1.2rem;}// Create new elementconst newParagraph = document.createElement("p");newParagraph.textContent = "This is a new paragraph";newParagraph.classList.add("dynamic-content");
// Create element with innerHTMLconst newDiv = document.createElement("div");newDiv.innerHTML = "<h3>Dynamic Title</h3><p>Dynamic content</p>";Adding Elements to the DOM
Section titled “Adding Elements to the DOM”const container = document.querySelector(".container");const newElement = document.createElement("p");newElement.textContent = "New paragraph";
// Append to endcontainer.appendChild(newElement);
// Insert at beginningcontainer.insertBefore(newElement, container.firstChild);
// Modern methods (newer browsers)container.append(newElement); // Can append multiple elementscontainer.prepend(newElement); // Add to beginning
// Insert adjacent to elementconst existingElement = document.querySelector("#existing");existingElement.insertAdjacentHTML("afterend", "<p>After the element</p>");existingElement.insertAdjacentHTML("beforebegin", "<p>Before the element</p>");Removing Elements
Section titled “Removing Elements”const elementToRemove = document.querySelector(".remove-me");
// Modern way (newer browsers)elementToRemove.remove();
// Legacy way (older browsers)elementToRemove.parentNode.removeChild(elementToRemove);
// Remove all childrenconst container = document.querySelector(".container");container.innerHTML = ""; // Quick way// orwhile (container.firstChild) { container.removeChild(container.firstChild);}Practical Examples
Section titled “Practical Examples”Dynamic List Manager
Section titled “Dynamic List Manager”<div class="container"><div id="task-list"><!-- Main content goes here --></div><input type="text" id="task-input"><button id="add-task">Add</button>
</div>.container {max-width: 60rem;border: 2px solid #1864ab;background-color: #e7f5ff;}
#task-list {display: flex;flex-direction: column;gap: 10px;}function createListManager() { const list = document.querySelector("#task-list"); const input = document.querySelector("#task-input"); const addButton = document.querySelector("#add-task");
function addTask() { const taskText = input.value.trim(); if (taskText === "") return;
// Create list item const listItem = document.createElement("li"); listItem.classList.add("task-item");
// Create task content const taskSpan = document.createElement("span"); taskSpan.textContent = taskText; taskSpan.classList.add("task-text");
// Create delete button const deleteButton = document.createElement("button"); deleteButton.textContent = "Delete"; deleteButton.classList.add("delete-btn"); deleteButton.onclick = () => listItem.remove();
// Assemble and add to DOM listItem.appendChild(taskSpan); listItem.appendChild(deleteButton); list.appendChild(listItem);
// Clear input input.value = ""; }
addButton.onclick = addTask;}
// Initialize when DOM is loadeddocument.addEventListener("DOMContentLoaded", createListManager);Image Gallery
Section titled “Image Gallery”<div id="gallery-container"> <h2>My Awesome Gallery</h2> <div id="gallery"></div></div>#gallery-container { padding: 20px; border: 1px solid #ccc; border-radius: 8px;}#gallery { display: flex; flex-wrap: wrap; gap: 15px;}.image-container { border: 1px solid #ddd; padding: 5px; border-radius: 4px; text-align: center;}.gallery-image { max-width: 150px; height: auto; cursor: pointer; border-radius: 4px;}.image-caption { margin-top: 5px; font-size: 14px; color: #555;}.lightbox-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.8); display: flex; justify-content: center; align-items: center; z-index: 1000;}.lightbox-image { max-width: 90%; max-height: 90%; border: 3px solid white; border-radius: 5px;}function createImageGallery(images) {const gallery = document.querySelector("#gallery");
images.forEach((imageData) => {// Create containerconst imageContainer = document.createElement("div");imageContainer.classList.add("image-container");
// Create imageconst img = document.createElement("img");img.src = imageData.src;img.alt = imageData.alt;img.classList.add("gallery-image");
// Create captionconst caption = document.createElement("p");caption.textContent = imageData.caption;caption.classList.add("image-caption");
// AssembleimageContainer.appendChild(img);imageContainer.appendChild(caption);gallery.appendChild(imageContainer);
// Add click handler for lightbox effectimg.onclick = () => showLightbox(imageData.src);});}
function showLightbox(imageSrc) {// Create lightbox overlayconst overlay = document.createElement("div");overlay.classList.add("lightbox-overlay");overlay.onclick = () => overlay.remove();
const img = document.createElement("img");img.src = imageSrc;img.classList.add("lightbox-image");
overlay.appendChild(img);document.body.appendChild(overlay);}
// Sample data - replace with your own imagesconst sampleImages = [{ src: '/src/assets/cute_dog.jpg', alt: 'A cute dog', caption: 'Cute Dog' },{ src: '/src/assets/doggo.jpg', alt: 'Another dog', caption: 'Doggo' },{ src: '/src/assets/houston.webp', alt: 'A cat', caption: 'Houston the cat' }];
// Initialize when DOM is loadeddocument.addEventListener("DOMContentLoaded", () => {// Ensure the gallery element exists before creating the galleryif(document.querySelector("#gallery")) { createImageGallery(sampleImages);}});Form Validator
Section titled “Form Validator”<form id="registration-form" novalidate> <h2>Registration</h2> <div class="form-group"> <label for="name">Name:</label> <input type="text" id="name" name="name" required minlength="2"> </div> <div class="form-group"> <label for="email">Email:</label> <input type="email" id="email" name="email" required> </div> <div class="form-group"> <label for="password">Password:</label> <input type="password" id="password" name="password" required minlength="6"> </div> <button type="submit">Register</button></form>#registration-form { max-width: 400px; padding: 20px; border: 1px solid #ccc; border-radius: 8px;}.form-group { margin-bottom: 15px;}label { display: block; margin-bottom: 5px;}input { width: 100%; padding: 8px; box-sizing: border-box; border: 1px solid #ccc; border-radius: 4px;}input.error { border-color: red;}.error-message { color: red; font-size: 12px; margin-top: 5px;}button { padding: 10px 15px; background-color: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer;}button:hover { background-color: #0056b3;}function setupFormValidation() { const form = document.querySelector("#registration-form"); const nameInput = document.querySelector("#name"); const emailInput = document.querySelector("#email"); const passwordInput = document.querySelector("#password");
function showError(input, message) { // Remove existing error const existingError = input.parentNode.querySelector(".error-message"); if (existingError) existingError.remove();
// Create error message const errorDiv = document.createElement("div"); errorDiv.classList.add("error-message"); errorDiv.textContent = message; errorDiv.style.color = "red"; errorDiv.style.fontSize = "14px";
// Add after input input.parentNode.insertBefore(errorDiv, input.nextSibling); input.classList.add("error"); }
function clearError(input) { const errorMessage = input.parentNode.querySelector(".error-message"); if (errorMessage) errorMessage.remove(); input.classList.remove("error"); }
function validateEmail(email) { // A simple check for an "@" and a "." is sufficient for this example. // Regular expressions are powerful but can be complex for beginners. return email.includes('@') && email.includes('.'); }
// Real-time validation nameInput.oninput = () => { if (nameInput.value.trim().length < 2) { showError(nameInput, "Name must be at least 2 characters"); } else { clearError(nameInput); } };
emailInput.oninput = () => { if (!validateEmail(emailInput.value)) { showError(emailInput, "Please enter a valid email"); } else { clearError(emailInput); } };
passwordInput.oninput = () => { if (passwordInput.value.length < 6) { showError(passwordInput, "Password must be at least 6 characters"); } else { clearError(passwordInput); } };}
// Initialize when DOM is loadeddocument.addEventListener("DOMContentLoaded", () => { if(document.querySelector("#registration-form")) { setupFormValidation(); }});Best Practices
Section titled “Best Practices”- Cache DOM queries: Store frequently used elements in variables
- Use event delegation: For dynamic content, attach events to parent elements
- Batch DOM operations: Minimize reflows and repaints
- Use document fragments: For multiple element creation
- Validate elements exist: Check if
querySelectorreturns null
Performance Tips
Section titled “Performance Tips”// ❌ Bad - queries DOM multiple timesdocument.querySelector("#list").appendChild(item1);document.querySelector("#list").appendChild(item2);document.querySelector("#list").appendChild(item3);
// ✅ Good - cache the elementconst list = document.querySelector("#list");list.appendChild(item1);list.appendChild(item2);list.appendChild(item3);
// ✅ Even better - use document fragmentconst fragment = document.createDocumentFragment();fragment.appendChild(item1);fragment.appendChild(item2);fragment.appendChild(item3);list.appendChild(fragment);Common Pitfalls****
Section titled “Common Pitfalls****”- Modifying DOM during iteration: Can cause elements to be skipped
- Memory leaks: Removing elements but keeping references
- XSS vulnerabilities: Using innerHTML with user input
- Assuming elements exist: Always check if querySelector returns null
Key Takeaways
Section titled “Key Takeaways”- DOM is your interface to HTML: JavaScript’s way to interact with web pages
- querySelector/querySelectorAll: Modern, flexible element selection
- classList: Clean way to manage CSS classes
- createElement/appendChild: Dynamic content creation
- Cache DOM queries: Store elements in variables for performance
- Always validate: Check if elements exist before using them
Practice Exercises
Section titled “Practice Exercises”- Create a dynamic table that can add/remove rows
- Build a color picker that changes page background
- Make a simple drag-and-drop interface
- Create a modal dialog system
- Build a real-time character counter for a textarea