DOM Manipulation
The Document Object Model (DOM) is a programming interface for HTML and XML documents. It represents the page as a tree of objects that can be manipulated with JavaScript.
Understanding the DOM
The DOM represents HTML as a tree structure:
<!DOCTYPE html>
<html>
<head>
<title>My Page</title>
</head>
<body>
<h1>Hello World</h1>
<p>This is a paragraph.</p>
<div>
<span>Span inside div</span>
</div>
</body>
</html>
This creates a tree:
document
└── html
├── head
│ └── title
└── body
├── h1
├── p
└── div
└── span
Selecting Elements
getElementById
let element = document.getElementById('myId');
getElementsByClassName
let elements = document.getElementsByClassName('myClass');
// Returns HTMLCollection
getElementsByTagName
let paragraphs = document.getElementsByTagName('p');
let allDivs = document.getElementsByTagName('div');
querySelector
// Selects first matching element
let element = document.querySelector('.myClass');
let link = document.querySelector('a[href="#"]');
let nested = document.querySelector('div > p');
querySelectorAll
// Selects all matching elements
let elements = document.querySelectorAll('.myClass');
// Returns NodeList
Working with Element Content
innerHTML
let div = document.querySelector('#content');
// Get HTML content
let html = div.innerHTML;
// Set HTML content
div.innerHTML = '<p>New content</p>';
// Append HTML
div.innerHTML += '<span>More content</span>';
textContent
let paragraph = document.querySelector('p');
// Get text content (ignores HTML tags)
let text = paragraph.textContent; // "Hello <strong>world</strong>"
// Set text content (escapes HTML)
paragraph.textContent = 'Hello <strong>world</strong>';
// Result: Hello <strong>world</strong>
innerText
// Similar to textContent but considers CSS styling
let visibleText = element.innerText;
Modifying Attributes
getAttribute and setAttribute
let link = document.querySelector('a');
// Get attribute
let href = link.getAttribute('href');
// Set attribute
link.setAttribute('href', 'https://example.com');
// Remove attribute
link.removeAttribute('title');
Direct Property Access
let input = document.querySelector('input');
// For standard attributes
input.value = 'New value';
input.disabled = true;
link.href = 'https://example.com';
// For data attributes
element.dataset.userId = '123';
element.dataset.customValue = 'value';
Modifying Styles
style Property
let element = document.querySelector('.box');
element.style.backgroundColor = 'red';
element.style.width = '200px';
element.style.height = '100px';
element.style.border = '1px solid black';
element.style.borderRadius = '5px';
CSS Classes
let element = document.querySelector('.box');
// Add class
element.classList.add('active');
// Remove class
element.classList.remove('hidden');
// Toggle class
element.classList.toggle('visible');
// Check if class exists
if (element.classList.contains('active')) {
// Do something
}
// Replace class
element.classList.replace('oldClass', 'newClass');
// Add multiple classes
element.classList.add('class1', 'class2');
// Remove multiple classes
element.classList.remove('class1', 'class2');
Creating and Adding Elements
createElement
let newDiv = document.createElement('div');
let newParagraph = document.createElement('p');
let newImage = document.createElement('img');
Setting Properties
let img = document.createElement('img');
img.src = 'image.jpg';
img.alt = 'Description';
img.className = 'thumbnail';
img.style.width = '200px';
Adding to DOM
let parent = document.querySelector('#container');
let newElement = document.createElement('p');
newElement.textContent = 'New paragraph';
// Append as last child
parent.appendChild(newElement);
// Insert before specific element
let reference = document.querySelector('#reference');
parent.insertBefore(newElement, reference);
// Insert at specific position
parent.insertAdjacentElement('beforeend', newElement);
// Using modern methods
parent.append(newElement); // Can append multiple
parent.prepend(newElement); // Add as first child
Removing Elements
removeChild
let parent = document.querySelector('#container');
let child = document.querySelector('#toRemove');
parent.removeChild(child);
remove (Modern)
let element = document.querySelector('#toRemove');
element.remove();
Replacing Elements
let oldElement = document.querySelector('#old');
let newElement = document.createElement('div');
newElement.textContent = 'New content';
// Replace
oldElement.parentNode.replaceChild(newElement, oldElement);
// Modern way
oldElement.replaceWith(newElement);
Cloning Elements
let original = document.querySelector('#original');
let clone = original.cloneNode(); // Shallow clone
let deepClone = original.cloneNode(true); // Deep clone (includes children)
// Add the clone to DOM
original.parentNode.appendChild(clone);
Traversing the DOM
Parent Navigation
let child = document.querySelector('#child');
let parent = child.parentNode;
let parentElement = child.parentElement; // Same as parentNode for elements
Child Navigation
let parent = document.querySelector('#parent');
// First and last child
let firstChild = parent.firstChild; // Includes text nodes
let lastChild = parent.lastChild;
let firstElement = parent.firstElementChild; // Only elements
let lastElement = parent.lastElementChild;
// All children
let children = parent.children; // HTMLCollection of elements
let childNodes = parent.childNodes; // NodeList including text nodes
Sibling Navigation
let element = document.querySelector('#middle');
// Previous sibling
let prevSibling = element.previousSibling;
let prevElement = element.previousElementSibling;
// Next sibling
let nextSibling = element.nextSibling;
let nextElement = element.nextElementSibling;
Event Handling
Adding Event Listeners
let button = document.querySelector('#myButton');
// Method 1: addEventListener
button.addEventListener('click', function(event) {
console.log('Button clicked!');
console.log('Event type:', event.type);
console.log('Target:', event.target);
});
// Method 2: Inline (not recommended)
button.onclick = function() {
console.log('Button clicked!');
};
Event Object
button.addEventListener('click', function(event) {
event.preventDefault(); // Prevent default action
event.stopPropagation(); // Stop event bubbling
console.log('Mouse X:', event.clientX);
console.log('Mouse Y:', event.clientY);
console.log('Which button:', event.button);
});
Event Types
let input = document.querySelector('input');
// Mouse events
element.addEventListener('click', handler);
element.addEventListener('dblclick', handler);
element.addEventListener('mousedown', handler);
element.addEventListener('mouseup', handler);
element.addEventListener('mousemove', handler);
// Keyboard events
input.addEventListener('keydown', handler);
input.addEventListener('keyup', handler);
input.addEventListener('keypress', handler);
// Form events
input.addEventListener('focus', handler);
input.addEventListener('blur', handler);
input.addEventListener('change', handler);
input.addEventListener('input', handler);
// Window events
window.addEventListener('load', handler);
window.addEventListener('resize', handler);
window.addEventListener('scroll', handler);
Event Delegation
// Instead of adding listeners to each li
document.querySelector('#list').addEventListener('click', function(event) {
if (event.target.tagName === 'LI') {
console.log('List item clicked:', event.target.textContent);
}
});
Removing Event Listeners
function handleClick() {
console.log('Clicked!');
}
button.addEventListener('click', handleClick);
// Remove specific listener
button.removeEventListener('click', handleClick);
// Remove all listeners (not straightforward)
button.onclick = null;
Form Handling
Getting Form Values
let form = document.querySelector('#myForm');
form.addEventListener('submit', function(event) {
event.preventDefault();
let formData = new FormData(form);
// Get individual values
let name = form.elements['name'].value;
let email = form.elements['email'].value;
// Or iterate through all fields
for (let [key, value] of formData.entries()) {
console.log(`${key}: ${value}`);
}
});
Dynamic Form Creation
function addInputField(container, name, type = 'text') {
let input = document.createElement('input');
input.type = type;
input.name = name;
input.placeholder = `Enter ${name}`;
let label = document.createElement('label');
label.textContent = name + ': ';
label.appendChild(input);
container.appendChild(label);
container.appendChild(document.createElement('br'));
}
let form = document.querySelector('#dynamicForm');
addInputField(form, 'username');
addInputField(form, 'email', 'email');
addInputField(form, 'password', 'password');
Performance Considerations
Minimize DOM Access
// Bad - multiple DOM accesses
let list = document.querySelector('#list');
for (let i = 0; i < 100; i++) {
list.innerHTML += '<li>Item ' + i + '</li>';
}
// Good - build string then update DOM once
let list = document.querySelector('#list');
let html = '';
for (let i = 0; i < 100; i++) {
html += '<li>Item ' + i + '</li>';
}
list.innerHTML = html;
Use Document Fragments
let fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
let li = document.createElement('li');
li.textContent = 'Item ' + i;
fragment.appendChild(li);
}
document.querySelector('#list').appendChild(fragment);
Debounce Events
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
window.addEventListener('resize', debounce(function() {
console.log('Window resized');
}, 250));
Common Patterns
Toggle Visibility
function toggleVisibility(selector) {
let element = document.querySelector(selector);
element.style.display = element.style.display === 'none' ? 'block' : 'none';
}
// Usage
document.querySelector('#toggleBtn').addEventListener('click', function() {
toggleVisibility('#content');
});
Dynamic List Management
let list = document.querySelector('#dynamicList');
function addItem(text) {
let li = document.createElement('li');
li.textContent = text;
let removeBtn = document.createElement('button');
removeBtn.textContent = 'Remove';
removeBtn.addEventListener('click', function() {
li.remove();
});
li.appendChild(removeBtn);
list.appendChild(li);
}
function clearList() {
list.innerHTML = '';
}
// Usage
document.querySelector('#addBtn').addEventListener('click', function() {
let input = document.querySelector('#itemInput');
if (input.value.trim()) {
addItem(input.value.trim());
input.value = '';
}
});
Modal Dialog
function showModal(content) {
let modal = document.createElement('div');
modal.className = 'modal';
modal.innerHTML = `
<div class="modal-content">
<span class="close">×</span>
${content}
</div>
`;
document.body.appendChild(modal);
// Close modal when clicking X or outside
modal.querySelector('.close').addEventListener('click', () => modal.remove());
modal.addEventListener('click', (e) => {
if (e.target === modal) modal.remove();
});
}
// Usage
document.querySelector('#showModal').addEventListener('click', function() {
showModal('<h2>Modal Title</h2><p>Modal content...</p>');
});
Best Practices
- Cache DOM queries when used multiple times
- Use event delegation for dynamic content
- Minimize DOM manipulations in loops
- Use semantic HTML for better accessibility
- Handle browser compatibility with fallbacks
- Clean up event listeners to prevent memory leaks
- Use data attributes for storing custom data
- Test across different browsers and devices
DOM manipulation is a core skill for front-end development. Understanding these techniques allows you to create dynamic, interactive web applications.
