How to Avoid For Loops that Manipulate the DOM in Javascript

For loops that interact with and manipulate the DOM may look simple, but can be a huge performance buster, depending on what is going on. But let’s look at best practices anyway and write better code.

Original Unoptimized Code

Here is our initial code. This for loop loops through a bunch of child elements and removes a class, which can trigger a style recalculation. There may be other code in the loop that can cause layout thrashing and style recalculation for each iteration of the loop!

const childElems = document.querySelectorAll('.child-elem');

for(let l = 0; l < childElems.length; l++) {
  childElems[l].classList.remove("bad-class");
  // do other potentially expensive DOM work...
}Code language: JavaScript (javascript)

Let’s look at two solutions to git ‘er optimized.

removeChild & appendChild

If we use removeChild, then we can do our work on the elements that are removed or detached from the DOM, and then add them back later with appendChild.

Note that a reference to the removed element must be kept for us to be able to modify and re-insert it later.

const someElem = document.querySelector('.some-elem');
const parentElem = someElem.parentElement;

const removedContainer = parentElem.removeChild(someElem);
const removedItems = Array.from(removedContainer.children);

for(let l = 0; l < removedItems.length; l++) {
  removedItems[l].classList.remove("red");
  // Do other potentially expensive DOM work...
}
// Insert the removed element back into the DOM
parentElem.appendChild(removedContainer);Code language: JavaScript (javascript)

View this code in a JSFiddle

cloneNode & replaceChild

If we use cloneNode and replaceChild, then we can achieve a similar result. Be warned that cloneNode does not copy any event listeners that may have been added by addEventListener to (I believe any) elements within the clonedNode.

const someElem = document.querySelector('.some-elem');
const clonedElem = someElem.cloneNode(true);
const clonedItems = Array.from(clonedElem.children);

for(let l = 0; l < clonedItems.length; l++) {
  clonedItems[l].classList.remove("bad-class");
  // Do other potentially expensive DOM work...
}
// Insert the removed element back into the DOM with replaceChild(new, old)
someElem.parentElement.replaceChild(clonedElem,someElem);Code language: JavaScript (javascript)

View this code in a JSFiddle

Leave a Comment