Portal Performance
dom

Insertar en el DOM dentro de un bucle

Meter 500 nodos uno a uno provoca 500 recálculos. Cómo bajar a uno.

El problema

Necesitas pintar una lista de 500 elementos. Escribes lo obvio: un bucle con createElement y appendChild. Funciona, pero se nota lento en móvil.

El motivo no es el bucle. Es que cada appendChild es una modificación al árbol del DOM, y el navegador tiene que estar preparado en cualquier momento para responder a preguntas sobre layout (offsetHeight, getBoundingClientRect). Aunque en la práctica intenta agrupar reflows, un patrón de 500 mutaciones seguidas es exactamente lo que le impide hacerlo.

El código “roto”

const container = document.getElementById("list");

for (let i = 0; i < 500; i++) {
  const item = document.createElement("div");
  item.textContent = "Elemento " + i;
  container.appendChild(item);
}

Las pistas

Ver la solución
const container = document.getElementById("list");
const fragment = document.createDocumentFragment();

for (let i = 0; i < 500; i++) {
  const item = document.createElement("div");
  item.textContent = "Elemento " + i;
  fragment.appendChild(item);
}

container.appendChild(fragment);

Un DocumentFragment es un contenedor ligero que no forma parte del documento visible. Puedes montar dentro toda la lista sin que el navegador reflowee nada, y cuando insertas el fragmento en el contenedor real, el DOM ve una sola inserción, no 500.

También sirve un array + spread, que expresa la misma idea de otra manera:

const container = document.getElementById("list");
const items = [];

for (let i = 0; i < 500; i++) {
  const item = document.createElement("div");
  item.textContent = "Elemento " + i;
  items.push(item);
}

container.append(...items);

El DocumentFragment es el patrón canónico de la plataforma; el array + spread lee bien y no necesita conocerlo. Cualquiera vale, siempre que se pase de N mutaciones a una.

Pruébalo

500 tarjetas, con y sin fragment

Pulsa los dos botones y compara. Ambos meten 500 elementos en el DOM; la diferencia está en cuántas veces el navegador tiene que replantear layout: 500 con la versión rota (una modificación por iteración), una sola conDocumentFragment. Abre la pestaña Performancede las devtools para verlo en el timeline.

— ms

Comprueba lo que has aprendido

  1. 1. ¿Por qué es lento insertar 500 nodos con appendChild dentro de un bucle?

  2. 2. ¿Qué hace un DocumentFragment y por qué ayuda aquí?

  3. 3. Al hacer `container.append(...items)` con 500 elementos, ¿cuántos reflows fuerza?

  4. 4. ¿Cuál de estas variantes también sirve para bajar los 500 reflows a uno?

  5. 5. ¿Qué es exactamente un “reflow”?