Primero leer, luego escribir: evitar el layout thrashing

Mezclar lecturas y escrituras del DOM en un mismo bucle obliga al navegador a recalcular el layout una y otra vez. Mejor agruparlas.

Cuando cambias un estilo, el navegador no recalcula el layout de inmediato. Lo marca como desactualizado y espera al siguiente fotograma. Pero si después lees un valor de geometría como offsetWidth, no le queda otra: tiene que recalcular el layout en ese momento para darte una respuesta correcta. Si esto ocurre dentro de un bucle, tienes layout thrashing.

La versión lenta

for (const el of items) {
  const width = el.parentNode.offsetWidth; // lectura: fuerza el layout
  el.style.width = width / 2 + 'px';       // escritura: invalida el layout
}

Cada iteración invalida el layout y la siguiente lo vuelve a forzar. Con unos cientos de elementos, esto puede costar fácilmente decenas de milisegundos.

La versión rápida

const widths = items.map((el) => el.parentNode.offsetWidth); // todas las lecturas
items.forEach((el, i) => {                                     // todas las escrituras
  el.style.width = widths[i] / 2 + 'px';
});

Mismo resultado, un solo layout. La regla es sencilla: primero mide todo lo que necesites y después cambia cosas.

Qué cuenta como lectura

Los sospechosos habituales son las propiedades y métodos que devuelven geometría o valores calculados:

Detectarlo

En el panel Performance de las herramientas de desarrollo, el thrashing aparece como muchos bloques morados pequeños de Layout dentro de una sola tarea, a menudo con un aviso de forced reflow que señala la línea de código responsable. Si el código tiene que reaccionar a cambios de tamaño, un ResizeObserver suele ser mejor opción que medir a mano.

← Todas las notas