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:
offsetTop,offsetWidth,offsetHeighty similaresclientWidth,clientHeight,scrollTop,scrollHeightgetBoundingClientRect()getComputedStyle(), cuando lees un valor que depende del layoutinnerText, que también depende del layout
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.