Принудительный синхронный layout: чтение геометрии после записи в DOM
Что происходит
Обычно layout происходит после завершения JavaScript, один раз за кадр. Но если вы изменили DOM и сразу читаете layout-свойство — offsetWidth, scrollHeight, getBoundingClientRect(), getComputedStyle() — браузер обязан остановиться и посчитать макет прямо здесь, синхронно, внутри вашей задачи, потому что ответ зависит от вашего несохранённого изменения.
Один принудительный layout дёшев на маленькой странице. На большом DOM или в цикле (что превращается в layout thrashing) он добавляет обработчику сотни миллисекунд.
Как распознать
- Длинный processing time в разбивке INP, при этом в DevTools Performance блоки Layout вложены внутрь скриптовой задачи (предупреждение «Forced reflow»).
- Long Animation Frames API показывает большой forcedStyleAndLayoutDuration у вашего скрипта.
- Обработчик одновременно меняет DOM и измеряет элементы.
Как починить
- Читайте всю геометрию до первой записи в DOM внутри задачи.
- Кэшируйте измерения, которые не меняются (размеры элементов, ширину контейнера), вместо повторного чтения на каждое событие.
- Если измерить после изменения необходимо — отложите чтение до requestAnimationFrame следующего кадра или используйте ResizeObserver / IntersectionObserver вместо опроса геометрии.
Проблема
grid.style.width = next + "px" // write: layout is now dirty
const rect = cell.getBoundingClientRect() // read → forced synchronous layout
Фикс
const rect = cell.getBoundingClientRect() // read first, layout is clean
grid.style.width = next + "px" // write after; browser lays out
// once, before the next paint
Просканировать свой сайт бесплатно →