JavaScript DOM Manipulation Techniques
Learn JavaScript DOM manipulation techniques; create, insert, replace, and remove elements with performance optimization.
DOM (Document Object Model) manipulation is a critical aspect of web development that lets you build dynamic, interactive pages by changing the document's structure, content, and styling from JavaScript. This guide starts with the core techniques for creating, inserting, replacing, and removing elements, then moves on to best practices and performance optimizations — batching, document fragments, and node cloning — that keep those changes fast.
If you have not selected an element yet, start with selecting DOM elements. To understand the tree you are modifying, see understanding the DOM nodes.
Core Manipulation Techniques
Every change to the DOM falls into one of four categories: creating nodes, inserting them, replacing them, or removing them.
Creating Elements
Use document.createElement() to build an element in memory, then set its content and attributes before inserting it into the page. A node you create is not visible until you attach it to the document.
const card = document.createElement('div');
card.className = 'card';
card.textContent = 'Hello, DOM!';
card.setAttribute('data-role', 'greeting');
console.log(card.outerHTML);
// <div class="card" data-role="greeting">Hello, DOM!</div>Prefer textContent over innerHTML when you insert plain text — it is faster and avoids the security risk of injecting unescaped HTML.
Inserting Elements
Once an element exists, attach it with one of these methods:
parent.append(node)— adds the node as the last child (also accepts plain strings).parent.prepend(node)— adds it as the first child.target.before(node)/target.after(node)— inserts a sibling before or aftertarget.parent.appendChild(node)— the classic API; appends a single node and returns it.parent.insertBefore(node, reference)— insertsnodebefore thereferencechild.
const list = document.createElement('ul');
const first = document.createElement('li');
first.textContent = 'First';
list.append(first);
const second = document.createElement('li');
second.textContent = 'Second';
list.append(second);
// Put a new item before the first one.
const top = document.createElement('li');
top.textContent = 'Top';
first.before(top);
console.log(list.children.length); // 3
console.log(list.firstElementChild.textContent); // TopFor inserting an HTML string at a precise position without re-parsing the whole parent, use insertAdjacentHTML():
const wrapper = document.createElement('div');
const box = document.createElement('div');
box.textContent = 'middle';
wrapper.append(box); // box must be in the tree to gain siblings
box.insertAdjacentHTML('beforebegin', '<span>before</span>');
box.insertAdjacentHTML('afterend', '<span>after</span>');
console.log(box.previousElementSibling.textContent); // before
console.log(box.nextElementSibling.textContent); // afterReplacing and Removing Elements
oldNode.replaceWith(newNode)— swapsoldNodefornewNodein place.element.remove()— detaches the element from the DOM.parent.replaceChild(newNode, oldNode)andparent.removeChild(child)— the older equivalents.
const parent = document.createElement('div');
const a = document.createElement('p');
a.textContent = 'old';
parent.append(a);
const b = document.createElement('p');
b.textContent = 'new';
a.replaceWith(b);
console.log(parent.innerHTML); // <p>new</p>
b.remove();
console.log(parent.innerHTML); // (empty)The modern methods (append, prepend, before, after, replaceWith, remove) are easier to read than the legacy appendChild/insertBefore/replaceChild/removeChild and accept multiple nodes and strings at once. Reach for them first; fall back to the legacy API only when supporting very old browsers.
Best Practices for DOM Manipulation
Minimize Direct DOM Access
Accessing the DOM can be slow because it may cause the browser to re-compute the layout and re-paint the elements. To minimize direct DOM access:
- Batch DOM updates together instead of performing multiple small updates.
- Use variables to store references to frequently accessed elements.
Optimize Event Handling
Attach event handlers efficiently:
- Use event delegation to reduce the number of event listeners.
- Avoid attaching too many event listeners directly to elements.
Clean Up Unused Elements
Remove elements that are no longer needed to free up memory and improve performance:
- Use
removeChildorremovemethods to delete elements from the DOM.
Minimizing Reflows and Repaints
What Are Reflows and Repaints?
- Reflows occur when the layout of a portion of the page changes, causing the browser to re-calculate the positions and sizes of elements.
- Repaints happen when the visual appearance of elements changes without affecting the layout (e.g., color changes).
Techniques to Minimize Reflows and Repaints
Batch Changes
Batch multiple changes together to avoid repeated reflows and repaints:
<!DOCTYPE html>
<html>
<head>
<title>Batching Changes</title>
</head>
<body>
<div id="content">Original Content</div>
<button id="update">Update Content</button>
<script>
document.getElementById('update').addEventListener('click', () => {
const content = document.getElementById('content');
content.style.display = 'none'; // Hide element to batch changes
content.innerHTML = 'Updated Content';
content.style.display = 'block'; // Show element after updates
});
</script>
</body>
</html>This example shows how to batch changes to a DOM element to minimize reflows and repaints. By hiding the element before making multiple updates and then showing it afterward, you can avoid intermediate reflows and repaints.
Use CSS Classes for Changes
Apply CSS changes using classes rather than directly manipulating styles:
<!DOCTYPE html>
<html>
<head>
<title>Use CSS Classes</title>
<style>
.hidden { display: none; }
.highlight { color: red; font-weight: bold; }
</style>
</head>
<body>
<div id="content">Hello World</div>
<button id="toggle">Toggle Highlight</button>
<script>
document.getElementById('toggle').addEventListener('click', () => {
const content = document.getElementById('content');
content.classList.toggle('highlight');
});
</script>
</body>
</html>This example demonstrates how to use CSS classes to apply multiple style changes at once, which is more efficient than modifying individual style properties. Toggling a class that changes multiple styles helps reduce the number of reflows and repaints.
Using Document Fragments for Performance
What Is a Document Fragment?
A Document Fragment is a lightweight container used to hold a group of nodes. It is not part of the main DOM tree, which means changes to it do not trigger reflows and repaints.
When performing multiple DOM manipulations, use DocumentFragment to batch your changes and append them to the DOM in a single operation. This approach minimizes reflows and repaints, significantly improving performance.
Example of Using Document Fragments
<!DOCTYPE html>
<html>
<head>
<title>Document Fragments</title>
</head>
<body>
<div id="list"></div>
<button id="populate">Populate List</button>
<script>
document.getElementById('populate').addEventListener('click', (event) => {
const fragment = document.createDocumentFragment();
for (let i = 1; i <= 25; i++) {
const item = document.createElement('div');
item.textContent = `Item ${i}`;
fragment.appendChild(item);
}
document.getElementById('list').appendChild(fragment);
event.target.disabled = true; // Disable the button
});
</script>
</body>
</html>This example creates 25 div elements and appends them to a Document Fragment. Only after all the elements are added to the fragment, the fragment is appended to the DOM in one operation. This approach minimizes reflows and repaints by updating the DOM only once.
Cloning Nodes
The cloneNode() Method
The cloneNode() method is used to create a copy of a node. It can clone the node itself or the node along with its children.
Cloning a Node Without Children
<!DOCTYPE html>
<html>
<head>
<title>Cloning Nodes</title>
</head>
<body>
<div id="original">Original Node</div>
<button id="clone">Clone Node</button>
<script>
document.getElementById('clone').addEventListener('click', () => {
const original = document.getElementById('original');
const clone = original.cloneNode(false); // Clone without children
clone.id = 'clone';
clone.textContent = 'Cloned Node';
document.body.appendChild(clone);
});
</script>
</body>
</html>This example demonstrates how to clone a node without its children using the cloneNode(false) method. The cloned node will copy the original node's attributes and text content, but not its child nodes.
Cloning a Node With Children
<!DOCTYPE html>
<html>
<head>
<title>Cloning Nodes with Children</title>
</head>
<body>
<div id="original">
Original Node
<span>Child Node</span>
</div>
<button id="clone">Clone Node with Children</button>
<script>
document.getElementById('clone').addEventListener('click', () => {
const original = document.getElementById('original');
const clone = original.cloneNode(true); // Clone with children
clone.id = 'clone';
document.body.appendChild(clone);
});
</script>
</body>
</html>This example demonstrates how to clone a node along with its children using the cloneNode(true) method. The cloned node will include the original node's content and all its descendant nodes.
Conclusion
Efficient DOM manipulation is crucial for creating performant web applications. Start by mastering the core methods for creating, inserting, replacing, and removing elements, then layer on the optimizations: batch your updates, prefer CSS classes over inline styles, use DocumentFragment for bulk inserts, and clone nodes when you need repeated structures. Together these techniques keep your pages responsive even as the DOM grows.
Related Topics
- DOM Manipulation — a broader walkthrough of reading and writing the document tree.
- Traversing the DOM — navigate between parents, children, and siblings.
- DOM Performance Optimization — go deeper on keeping manipulation fast.
- Event Handling in the DOM — wire up interactions efficiently.