Layout thrashing (também conhecido como layout síncrono forçado ou forced reflow) ocorre quando o JavaScript alterna repetidamente entre escrever no DOM e ler propriedades de layout, forçando o navegador a recalcular o layout de forma síncrona.
Gatilhos de Invalidação de Layout
As propriedades e métodos DOM abaixo forçam o recálculo síncrono da geometria computada sempre que o layout tiver sido invalidado por alterações anteriores no DOM.
Propriedades e Métodos de Elemento
Geometria e Dimensões da Caixa
clientHeight,clientWidthclientLeft,clientTopoffsetHeight,offsetWidthoffsetLeft,offsetTopoffsetParentscrollHeight,scrollWidthscrollLeft,scrollTop
Consultas de Caixa Delimitadora
getBoundingClientRect()getClientRects()
Ações de Rolagem
scrollIntoView()scrollIntoViewIfNeeded()scrollByLines()scrollByPages()
Texto e Foco
innerTextouterTextfocus()(força anel de foco e ajustes de rolagem)
Propriedades de Evento de Mouse
Consultar coordenadas específicas em uma instância de MouseEvent pode acionar o cálculo de layout em alguns motores de navegador:
layerX,layerYoffsetX,offsetY
Métodos e Propriedades de Window
window.getComputedStyle()(ao consultar valores dependentes de layout como largura, altura, margem ou transform)window.scrollBy()window.scrollTo()window.scrollX,window.scrollY
Frame, Documento e Imagem
- Consultar
widthouheightem uma imagem antes que o layout estabilize - Acessar
document.body.scrollHeight
Operações com jQuery
Muitos métodos auxiliares do jQuery leem valores de layout computados internamente:
// Dispara reflow
$(elem).offset();
$(elem).offsetParent();
$(elem).position();
$(elem).scrollLeft();
$(elem).scrollTop();
$(elem).css('width'); // Qualquer cálculo de estilo dependente do layout
$(elem).text();
// Pseudoseletores que inspecionam visibilidade geométrica
$(':hidden');
$(':visible');
$(':contains');
Boas Práticas para Evitar Layout Thrashing
1. Agrupar Leituras e Escritas em Lote
Sempre execute primeiro todas as medições do DOM e depois agrupe todas as alterações no DOM.
// RUIM: Intercalar leitura e escrita força layout a cada iteração
elements.forEach((el) => {
const width = el.clientWidth; // LEITURA (força layout)
el.style.width = width * 2 + 'px'; // ESCRITA (invalida layout)
});
// BOM: Leituras em lote e depois escritas em lote
const widths = elements.map((el) => el.clientWidth); // LEITURA EM LOTE
elements.forEach((el, index) => {
el.style.width = widths[index] * 2 + 'px'; // ESCRITA EM LOTE
});
2. Usar requestAnimationFrame
Agende alterações de estilo para o próximo quadro de animação para que o navegador otimize o ciclo de renderização:
const height = element.clientHeight; // Ler durante a fase atual
window.requestAnimationFrame(() => {
element.style.height = height + 50 + 'px'; // Escrever no próximo quadro
});
3. Usar CSS Transforms e Opacity
Prefira animar propriedades CSS tratadas na thread de composição (transform, opacity) em vez de propriedades de layout (top, left, width, height).