Перейти к содержанию

Неожиданные сдвиги и Cumulative Layout Shift

Картинка и шрифт могут быть доставлены быстро, но изменить положение уже читаемого абзаца. Для пользователя это самостоятельная проблема: он теряет строку или нажимает не на ту ссылку. В этом уроке исследуем неожиданные сдвиги speed-lab и свяжем их с исходной причиной, а не только с элементом, который оказался перемещён.

Baseline намеренно вставляет изображение без заранее заданного размера и позднее добавляет учебное объявление. Таймер объявления равен 1800 миллисекундам — это условие демонстрации, не замер ProfessorWeb. Код автором не запускался. Значение CLS и фактическая видимость движения должны быть получены читателем в собственной ручной сессии.

Что означает сдвиг

Сдвиг связан с изменением положения видимых элементов между кадрами. Само добавление нового элемента ещё не обязательно создаёт такой результат: если он занимает свободное место и не двигает существующее содержимое, важного перемещения может не быть. Поэтому смотрим на конкретную геометрию страницы, а не объявляем любую динамическую вставку ошибкой.

CLS учитывает неожиданное движение через группы событий. В текущем определении оценивается наибольшая группа с короткими промежутками между сдвигами и ограниченной продолжительностью окна. Простая сумма всех записей за весь срок страницы не является корректной заменой метрики. Порог хорошего значения — не более 0,1 на 75-м процентиле соответствующей группы посещений. Определение CLS.

После явного действия пользователя некоторые изменения могут относиться к ожидаемому взаимодействию, но исключение имеет конкретные правила и временные границы. Нельзя считать любую позднюю работу, начатую нажатием, автоматически безвредной для метрики. Кроме того, удобство чтения шире формальной оценки: перемещение может раздражать человека даже при особенностях учёта.

Для speed-lab основной сценарий прост: обычный переход и чтение первого абзаца без прокрутки. Сохраняйте viewport и одинаковые действия, иначе видимая область изменит наблюдение. У широкого окна объявление может двигать другой набор элементов, чем у узкого. Запись должна относиться к определённому условию, а не к «всей странице вообще».

Перемещённый элемент и причина

Performance может показать абзац или карточку среди затронутых элементов. Однако источник движения часто находится выше: изображение получило высоту, объявление заняло пространство или заголовок изменил число строк после замены шрифта. Для решения требуется найти эту причину. Иначе вы начнёте фиксировать положение абзаца вместо правильного размещения предыдущего блока.

У baseline пустой #hero сначала не резервирует высоту. Когда появляется схема, следующие элементы получают новое положение. Для независимого эксперимента задайте размеры раннему img, как в предыдущих уроках, и оставьте остальные условия. Ожидаемый результат — место под изображение известно до доставки; фактическое изменение сдвигов оценивается по вашей записи.

<img class="hero" src="../assets/delivery-800.v1.png"
     width="1200" height="640"
     alt="Три стадии доставки: документ, ресурсы и отображение">

При width: 100% и height: auto браузер использует отношение сторон для размещения изображения в колонке. Если CSS затем назначит несовместимую высоту или один responsive-вариант имеет другое кадрирование, резерв может оказаться неправильным. Метаданные и оформление должны описывать один и тот же объект, поэтому проверяйте результат на всех вариантах ширины.

Место для позднего объявления

Уведомление в обоих вариантах добавляется после одинакового искусственного ожидания. Candidate имеет контейнер .notice-slot с минимальной высотой. Следующий CSS используется в стенде и показывает принцип резервирования:

.notice-slot {
  min-height: 6rem;
}
.notice {
  margin: 0;
  padding: 1rem;
  box-sizing: border-box;
}

Шесть rem — учебное решение для короткого текста, а не универсальный размер рекламы. При увеличении шрифта или длинном переводе объявление способно занять больше. Если резерв мал, нижний контент всё равно сдвинется. Если слишком велик, появится лишнее пустое пространство. Поэтому фиксированный резерв выбирают по известному диапазону содержимого и реальным требованиям чтения.

Объявление создаётся элементом p, поэтому его стандартные внешние отступы сброшены. Верхний margin мог бы схлопнуться через контейнер и добавить место снаружи уже зарезервированной области. Внутренний padding остаётся частью объявления. Это конкретное применение правил схлопывания отступов.

Не задавайте height с обрезанием текста ради нулевого движения. Такой приём может скрыть важное сообщение и ухудшить доступность. Минимальная высота позволяет содержимому расти, но оставляет ограничение, которое нужно проверить. Другой вариант — заранее показывать сам контейнер с понятным состоянием загрузки; выбор зависит от продукта и не определяется метрикой автоматически.

В настоящем проекте сторонний виджет способен менять размер несколько раз. Тогда нужно изучать договор размеров и способ включения в документ. Наше короткое объявление показывает одну вставку; оно не воспроизводит рекламный аукцион или все состояния стороннего iframe. Эти границы нужно сохранить при переносе учебного решения в приложение. Способы уменьшения CLS.

Диагностика записей

Для локального исследования можно наблюдать layout-shift при наличии поддержки. Следующий фрагмент показывает отдельные события, а не рассчитывает полноценный CLS; автор его не выполнял:

if (typeof PerformanceObserver === "function" &&
    PerformanceObserver.supportedEntryTypes.includes("layout-shift")) {
  const observer = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      console.log({ value: entry.value, recentInput: entry.hadRecentInput,
                    sources: entry.sources });
    }
  });
  observer.observe({ type: "layout-shift", buffered: true });
}

Сведения о поддержке проверяются для конкретного типа. Наличие самого PerformanceObserver не гарантирует все записи во всех браузерах. Если событие отсутствует, нельзя делать вывод о нулевом CLS без учёта инструмента. buffered помогает получить доступные ранее записанные события, но не создаёт отсутствующую реализацию API. PerformanceObserver.

Сопоставьте диагностическую строку с кадрами и моментом вставки. Если двигался первый абзац после объявления, причиной могло быть расширение контейнера над ним. Если менялась геометрия заголовка, вернитесь к проверке шрифта. Такой разбор сохраняет причинную связь и позволяет изменить нужный блок без закрепления всего документа на абсолютных координатах.

Изменение содержания без измеренного выигрыша

Размер резерва связан с предсказуемостью блока. Если уведомление может быть произвольной длины, одной константы недостаточно для полного договора. Нужно ограничить содержание, выбрать известный шаблон либо определить другую область размещения. В учебнике короткое объявление задано заранее, поэтому минимальная высота позволяет исследовать одну причину. Перенос этого свойства в настоящую систему требует проверки реальных состояний.

Не объявляйте нулевое значение обещанием любого окна. При увеличении текста строки становятся выше, а в узкой колонке переносов больше. Даже если обычный desktop-вариант выглядит устойчиво, мобильный сценарий способен показать движение. Выберите характерные условия и проверьте их отдельно, сохраняя читаемость сообщения. Связь с доступностью здесь важнее формального удаления одного события.

Когда источник исчезает или приходит пустой ответ, решите, что делать с резервом. Немедленное схлопывание большого места тоже может сдвинуть текст. Иногда разумнее сохранить область до завершения сценария, иногда допустима другая композиция. Это продуктовый договор, который должен быть понятен по содержимому страницы. CSS помогает выразить решение, но не выбирает его автоматически.

В конце сравните вариант с резервом и без него при одном сценарии. Проверьте также увеличенный текст, узкое окно и неудачную доставку изображения. Сохраните наблюдение о чтении, даже если цифровая оценка кажется небольшой. Устойчивое положение абзацев завершает загрузочную часть задачи; далее рассмотрим реакцию фильтра, где задержка может возникнуть уже после полного показа страницы.

Оглавление курса