There are a number of uses cases where we need to create DOM nodes and append them to a list of nodes.

Think about WhatApp Web, scroll up, older messages are populated on the chat window.

Or may be Instagram, scroll down, new posts keep adding up in an infinite scroll setting. There are a lot of performance aspects associated with such infinite scroll systems - DOM recycling, virtualisation, lazy load etc.

We would however keep this discussion limited to appending DOM nodes. You can ignore the other advanced aspects of infinite scroll systems.

The Problem

Let's assume we have a huge list of messages that we fetch from the API. We need to show the list of messages inside the container as shown below. We could do it in a way shown below. There is however a performance problem linked to this way of solving it. Can you identify the problem?

<div class="container">
  <!-- The messages should be added dynamically here -->
</div>
const list = [
  'Some message',
  'Another message',
  'Yet another message',
  ..
  ..
  ..
];

const appendMessages = (list) => {
  const container = document.querySelector('.container');
  list.forEach(item => {
	const elem = document.createElement('div');
    elem.classList.add('post');
    elem.innerText = item;
    container.appendChild(elem);
  });
};

appendMessages(list);

There are two things that make this part of the code expensive: container.appendChild(elem).

1) DOM operations are expensive. We are manipulating the DOM for every item we have in the list.

2) Every time we do an append, browser has to recalculate the dimensions and geometry of the elements on a page, which is a blocking operation. This is generally referred to as a reflow or layout.

Can you improve the performance of this code?

We need a way where we can minimise DOM interactions. One nice and readable way to do it is to use document.createDocumentFragment().

createDocumentFragment() creates a virtual DOM node in the memory, not in the actual DOM. We can append the list of elements to this virtual container. As a final operation, we can attach this virtual node to our container. This virtual DOM does not show up in the DOM. In fact, it is replaced by its children when attached to the DOM. Same output with improved performance.

See modified code below:

const appendMessages = (list) => {
  const container = document.querySelector('.container');
  const frag = document.createDocumentFragment();
  list.forEach(item => {
	const elem = document.createElement('div');
    elem.classList.add('post');
    elem.innerText = item;
    frag.appendChild(elem);
  });
  container.appendChild(frag);
};

Doing so we are able to reduce the number of DOM interactions from n(where n is the number of items in the list) to 1.

Anytime you see a problem where a list of nodes has to be appended to the DOM, make use of createDocumentFragment(). You'll definitely get extra points for that.


Found the post helpful? Don't forget to subscribe below.

P.S.: I am working on a list of no nonsense Top 100 popular Javascript Interview questions, which I expect to finish sometime within a few weeks. If you don't want to miss that out, you should definitely subscribe.