El layout thrashing (también llamado diseño síncrono forzado o forced reflow) ocurre cuando JavaScript alterna repetidamente entre escribir en el DOM y leer propiedades geométricas, obligando al navegador a recalcular el diseño de forma síncrona.
Disparadores de invalidación de layout
Las siguientes propiedades y métodos del DOM fuerzan el recálculo síncrono de la geometría siempre que el diseño haya sido invalidado por mutaciones previas.
Propiedades y métodos de elementos
Geometría y dimensiones de caja
clientHeight,clientWidthclientLeft,clientTopoffsetHeight,offsetWidthoffsetLeft,offsetTopoffsetParentscrollHeight,scrollWidthscrollLeft,scrollTop
Consultas de cuadros delimitadores
getBoundingClientRect()getClientRects()
Acciones de desplazamiento
scrollIntoView()scrollIntoViewIfNeeded()scrollByLines()scrollByPages()
Texto y foco
innerTextouterTextfocus()(fuerza anillo de foco y ajustes de desplazamiento)
Propiedades de eventos de ratón
Consultar ciertas coordenadas en una instancia MouseEvent puede desencadenar cálculos de diseño en algunos motores:
layerX,layerYoffsetX,offsetY
Métodos y propiedades de Window
window.getComputedStyle()(al consultar propiedades dependientes del diseño como ancho, alto o márgenes)window.scrollBy()window.scrollTo()window.scrollX,window.scrollY
Frame, documento e imagen
- Consultar
widthoheightde una imagen antes de estabilizarse el diseño - Acceder a
document.body.scrollHeight
Operaciones con jQuery
Muchos métodos auxiliares de jQuery leen internamente valores de diseño calculados:
// Desencadena reflow
$(elem).offset();
$(elem).offsetParent();
$(elem).position();
$(elem).scrollLeft();
$(elem).scrollTop();
$(elem).css('width'); // Cualquier cálculo de estilo dependiente del diseño
$(elem).text();
// Pseudoselectores que inspeccionan visibilidad geométrica
$(':hidden');
$(':visible');
$(':contains');
Buenas prácticas para evitar el Layout Thrashing
1. Agrupar lecturas y escrituras por lotes
Realiza siempre todas las mediciones del DOM primero y luego aplica las modificaciones juntas.
// MAL: Intercalar lectura y escritura fuerza recalcular el layout en cada iteración
elements.forEach((el) => {
const width = el.clientWidth; // LECTURA (fuerza layout)
el.style.width = width * 2 + 'px'; // ESCRITURA (invalida layout)
});
// BIEN: Lecturas por lotes y luego escrituras por lotes
const widths = elements.map((el) => el.clientWidth); // LECTURA POR LOTES
elements.forEach((el, index) => {
el.style.width = widths[index] * 2 + 'px'; // ESCRITURA POR LOTES
});
2. Usar requestAnimationFrame
Programa escrituras de estilo en el siguiente fotograma para que el navegador optimice el ciclo de pintado:
const height = element.clientHeight; // Leer durante la fase actual
window.requestAnimationFrame(() => {
element.style.height = height + 50 + 'px'; // Escribir en el siguiente fotograma
});
3. Usar transformaciones CSS y opacidad
Prioriza animar propiedades CSS gestionadas por el compositor (transform, opacity) en lugar de propiedades de diseño (top, left, width, height).