CLS в Core Web Vitals: как улучшить

Что такое CLS, какое значение метрики считается хорошим, часто встречающиеся ошибки и методы их исправления.

В новом видео Google Search Central Патрик Кеттнер рассказал, что такое CLS или совокупный сдвиг макета и какое значение метрики считается хорошим по мнению Google. Патрик также перечислил часто встречающиеся ошибки и показал, как их исправить.

Что такое CLS

Cumulative Layout Shift или CLS – это метрика Core Web Vitals, которая оценивает визуальную стабильность страницы. Если при загрузке на странице происходят какие-либо «скачки» и непредвиденные сдвиги элементов, то их называют «layout shift» или «сдвиг макета», поэтому по-русски метрику называют «совокупный сдвиг макета».

Макетом считается совокупность видимых элементов страницы – изображений, текста, видео, кнопок, иконок – их расположение и то, как они сочетаются друг с другом.

Иногда при изменении содержимого страницы нужно изменить макет, чтобы не допустить его непредвиденный «сдвиг». Например, при вставке рекламы, загрузке видео или изменении размера элементов. Всё, что уже есть на странице, нужно сместить, чтобы новые элементы поместились.

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

В этой ситуации браузер считает, что контент над изображением менять не нужно. А контент под изображением необходимо сместить вниз, чтобы освободить место для загрузки изображения. Текст статьи сдвигается вниз на половину видимого объёма страницы. Это означает, что 50 % контента, который видят пользователи, сместилось. В этом случае CLS будет составлять 0,5, то есть 50 %.

CLS показывает на сколько процентов изменилась видимая часть страницы. Но есть несколько важных моментов.

Метрика считает совокупность сдвигов

Ранее на примере статьи в блоге мы говорили, что CLS страницы составляет 0,5. Это верно, но только если на странице больше ничего не сдвигается или по крайней мере не сдвинулось до того, как мы успели закрыть страницу. Но если при прокрутке произойдут другие сдвиги, то CLS может сильно увеличиться.

Браузер будет отслеживать сдвиги на протяжении всего периода просмотра страницы пользователем. Как только пользователи уйдут со страницы, браузер сообщит о самом большом зафиксированном значении CLS.

Общее значение CLS формируется на основе данных о взаимодействии всех пользователей, посетивших URL.

Иногда изменения – это хорошо

Если страница никогда не меняется – значит она бесполезна. На сдвиги макета, которые вызваны действиями пользователей – нажатием, кликами, вводом текста – дана 500 миллисекундная отсрочка.

Это так называемый «льготный период» для изменений в макете, который необходим для ответа на действие пользователя.

«Льготный период» CLS
«Льготный период» CLS

После этого начинается отсчёт сессии CLS. Начиная с 501-й миллисекунды и далее, все произошедшие сдвиги макета суммируются и потенциально могут быть использованы для окончательной оценки CLS.

Эта сессия будет оставаться открытой до тех пор, пока будет происходить сдвиг макета видимых элементов. Все дополнительные сдвиги суммируются.

Например, какая-то страница на сайте имеет похожий на статью в блоге макет – заголовок и основной текст. Но на этот раз на сайте есть два более мелких изображения. Как и в примере ранее, изображения загружаются медленно – содержимое страницы смещается при загрузке этих изображений.

Но в отличие от предыдущего примера, первое изображение смещается лишь на небольшую величину –
5 %. CLS равен 0,05. Но через несколько сотен миллисекунд загружается второе изображение. Ему также нужно немного места, поэтому содержимое страницы ещё больше опускается вниз. Представим, что второе изображение сдвигает макет на 2,5 %. Поскольку это произошло почти сразу же после последнего сдвига макета, то значения суммируются в одно значение – 7,5 % или 0,075.

Окно сеанса будет оставаться открытым, пока не пройдет более одной секунды с момента последнего сдвига макета или пока не пройдет пять секунд с момента создания этого окна сеанса.

Любой сдвиг макета после этого будет считаться в отдельном окне сессии и не суммируется со значением предыдущей сессии.

Не все взаимодействия имеют «льготный период»

Не все действия на сайте дают отсрочку в 500 миллисекунд. Например, прокрутка или перемещение мыши не учитываются в «льготном периоде».

Это имеет смысл, потому что вряд ли пользователь ожидает, что макет будет меняться во время прокрутки страницы.

Не все изменения макета – это плохо

Содержимое страницы можно перемещать, например, с помощью CSS анимации или трансформации. Когда вы используете атрибут transform в CSS, элемент перемещается на свой собственный слой.

Это означает, что его положение не влияет на расположение других частей страницы. Он как бы «плавает» поверх всего остального. И поскольку ничего не смещается, это не будет учитываться в CLS.

Невидимые элементы не учитываются

CLS предназначен только для видимых элементов страницы. Если пользователь не видит элемент, то не имеет значения, сдвигается ли он.

Как и другие метрики Core Web Vitals, CLS стремится к тому, чтобы люди получали наилучший пользовательский опыт. Так что если нужно что-то передвинуть или выполнить отложенную загрузку контента, то вы можете реализовать это на своём сайте.

Если пользователь этого не видит и это не влияет на сдвиги других элементов, то это не отразится на CLS.

Главное помнить, что хороший показатель CLS означает, что то, что пользователи видят – стабильно и предсказуемо.

Каким должен быть CLS

Значение CLS может быть представлено в диапазоне от 0 до 1.

В идеальном мире CLS должен быть равен нулю, потому что почти никогда нет необходимости в сдвигах макета. Но это может получиться только в идеальных условиях.

Показатель, к которому следует стремиться – 75 % ваших URL-адресов имеют CLS 0,1 или меньше.

Хорошее значение CLS
Хорошее значение CLS

Если вы ниже этого показателя, то у вас все отлично. Если выше – читайте дальше.

Как узнать CLS

Как и все другие показатели Core Web Vitals, CLS основан на реальном опыте взаимодействия реальных пользователей со страницей. Эти данные Google называет Field data или Данные наблюдений.

Данные по CLS можно посмотреть через вашу собственную аналитику или в отчёте Core Web Vitals в Search Console.

Search Console, Page Experience и Core Web Vitals имеют один и тот же источник результатов CLS – CrUX, или Отчёт о пользовательском опыте Chrome.

Что такое Chrome User Experience Report (CrUX)

Данные в CrUX не обновляются мгновенно. После публикации страницы может пройти до месяца, прежде чем появятся Field data.

Но не нужно ждать месяц, чтобы получить представление о том, каким будет значение CLS. Используя такие инструменты, как Lighthouse и Chrome DevTools, вы можете быстро получить те же Field data, которые отображаются в Search Console, а также генерировать Lab data – Данные при имитации загрузки страницы.

Lab data – это наилучшее предположение о том, как будут выглядеть Field data. Эти данные собираются в контролируемой среде с заранее определённым устройством и скоростью интернета. Они могут быть полезны, чтобы понять, в каком направлении двигаться.

Помните, что Lab data – это только предположение. А Field data – это метрика реального пользовательского опыта, поэтому поисковик будет учитывать именно это значение.

Если у ваших пользователей высокотехнологичные устройства или медленное интернет-соединение, то вы, скорее всего, увидите большую разницу между значениями Lab data и Field data.

Lab data может быть очень полезными, но после того, как вы опубликуете страницу, намного полезнее собирать данные CLS в Field data.

Почему значения метрик в Lab data и Field data могут отличаться

Отслеживание CLS с помощью JavaScript

Как и другие показатели Core Web Vitals, мы можем измерить CLS с помощью JavaScript, используя PerformanceObserver:

{
  let cls = 0;

  new PerformanceObserver((entryList) => {
    for (const entry of entryList.getEntries()) {
      if (!entry.hadRecentInput) {
        cls += entry.value;
        console.log('Current CLS value:', cls, entry);
      }
    }
  }).observe({type: 'layout-shift', buffered: true});
}

Это относительно новый API браузера. Он позволяет подписываться на события в браузере, которые влияют на производительность. У нас нет возможности непосредственно наблюдать сдвиги макета, но мы получим все необходимые данные, чтобы понять это.

В данном случае мы будем подписаны на событие «сдвиг макета». Теперь каждый раз, когда в браузере происходит событие «сдвиг макета», вызывается код.

Это позволит понять, какой именно сдвиг макета видит конкретный пользователь. После вызова функции мы получаем список всех сдвигов макета, которые были зафиксированы браузером. Для этого списка мы используем метод getEntries(), чтобы получить массив записей. Потом каждую запись, то есть каждый сдвиг макета, можно будет рассмотреть по-отдельности.

С помощью свойства hadRecentInput мы сможем проверить, было ли событие зафиксировано правильно. Если hadRecentInput в значении true, то мы находимся в «льготном» 500-ти миллисекундном периоде и эту запись можно проигнорировать. Но если hadRecentInput в значении false, то мы можем захватить entry.value и добавить его к нашему CLS.

Вы могли заметить, что этот счётчик постоянно дополняется новыми записями. Но разве мы чуть ранее не говорили про сессии?

На самом деле – это то, как раньше рассчитывался CLS: на основе суммы всех сдвигов. Но теперь CLS рассчитывается на основе окон сессий, как мы объяснили выше.

Если вы хотите отслеживать значение CLS через API по-новому, то можно обновить код для его отслеживания с помощью всего лишь нескольких дополнительных переменных.

Обновлённая версия кода

Изменение видимости (‘visibilitychange’)

В браузере есть ещё одно событие, которое называется «изменение видимости».

Оно срабатывает всякий раз, когда видимость страницы меняется, например, когда пользователь сворачивает окно, закрывает страницу или переходит на другую вкладку.

Это событие можно использовать, чтобы расширить отчётность. Если ‘visibilitychange’ в значении hidden, то мы просим записать это в PerformanceObserver. В результате мы получим список всех записей, которые не были обработаны кодом.

Затем каждую запись можно будет рассмотреть по-отдельности, как мы делали ранее. После мы вызываем функцию sendCLSToTheServer.

Единственная странность кода заключается в следующем: чтобы гарантировать, что CLS всегда отправляется, когда люди уходят со страницы, мы используем событие ‘visibilitychange’.

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

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

Помните, что значение CLS может быть только одно. Для получения более подробной информации о том, как это достигается, ознакомьтесь с JavaScript-библиотекой Web Vitals.

Как улучшить CLS

Сдвиги макета могут происходить из-за нескольких причин:

  • для изображения не прописана высота и ширина;
  • для элементов iframe и embeds не указана высота и ширина;
  • для динамически загружаемого контента не предусмотрено специальное место с заданной высотой и шириной;
  • веб-шрифты и иконки вызывают скачок нестилизованного или невидимого текста.

Изображения

Если к изображению не прописаны атрибуты высоты и ширины, то для него просто фиксируется определённое место. Оно сильно отличается от размера того пространства, которое картинка на самом деле занимает после загрузки. Из-за этого контент страницы «скачет», вызывая сдвиг макета.

Самый лучший способ решить эту проблему – прописать атрибуты ширины и высоты для изображения:

<img id="ad" scr="BannerAd!!.bmp" height="240" width="450" />

Раньше этот способ активно использовали. Но при появлении адаптивного дизайна и экранов с высоким разрешением стало более полезно прописывать атрибуты в CSS или предоставлять браузеру возможность самостоятельно прописать эти параметры:

#ad {
height: calc(75vw/2);
width: 75vw;
}

Сейчас современные браузеры автоматически определяют размер изображения на основе соотношения сторон, т. е. делят ширину изображения на его высоту:

#ad {
aspect-ratio: 1 / 2;
}

Мы всё ещё можем добавлять стили изображению. Например, прописать ширину 100 % в CSS. Но теперь браузеры будут автоматически резервировать правильную высоту, основываясь на соотношении сторон изображения.

Это предотвратит появление сдвигов макета, даже если у пользователя медленный интернет и изображение долго загружается.

Элементы iframes и embeds

В эти элементы загружается различный контент: рекламные слоты, виджеты для видеоплеера – всё, что загружается с других доменов и серверов.

Мы не можем контролировать то, что происходит внутри iframe, но мы можем контролировать то, что происходит вокруг него. Так же, как и для изображений, для этих элементов нужно зарезервировать место, которое они будут занимать.

Когда вы установили значения высоты и ширины для iframe, браузер резервирует для него место. Это предотвращает любое смещение макета.

<div class="iframeWrapper">
	<iframe scr="HiDad.html" title="hello!" height="240" width="450" />
.iframeWrapper {
height: 240px;
width: 450px;
}

Иногда рекламная сеть, от которой мы получаем рекламу, не совсем правильно указывает нам размер для своего слота. Он может быть на пару пикселей больше, чем нужно. Поэтому я люблю добавлять в стили оverflow: hidden, чтобы предотвратить размещение в слоте чего-то, превышающего его размер. Правда, это не всегда удобно.

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

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

Здесь идёт речь об об изоляции iframes и embeds, чтобы они не занимали место, которое не учтено в дизайне. Независимо от того, будем ли мы вырезать непредвиденный контент, например, рекламу, или просто увеличивать размер, есть способ избежать смещения макета при динамически загружаемом контенте.

Динамически загружаемый контент

Не весь динамически загружаемый контент помещается в iframe. Если на вашем сайте есть бесконечный прокручивающийся список статей или товаров или если вы выполняете отложенную загрузку, то вам нужно убедиться, что вы зарезервировали место для контента с определённой высотой и шириной.

Как и в случае с iframe, мы можем надеяться, что сервер будет возвращать контент, который занимает именно это пространство. Но в идеале мы должны разработать дизайн таким образом, чтобы он был достаточно гибким и позволял разместить контент чуть больше или чуть меньше указанного размера.

Наличие гибких, но неизменных атрибутов ширины и высоты является ключевым фактором для получения минимального балла CLS.

Шрифты и иконки

До сих пор все ошибки можно было свести к следующему: убедитесь, что для прямоугольников прописаны атрибуты высоты и ширины, которые не меняются.

Но что делать, если меняется содержимое этого прямоугольника? В этом смысле шрифты представляют собой проблему для CLS.

Мы не можем явно задать ширину или высоту текста. Поэтому к шрифту или символам, которые вызывают смещение макета, нужно подходить с другой стороны.

Самое простое решение – не использовать шрифты. В случае с иконками лучше использовать SVG. Это предоставляет больше возможностей для дизайна, а также гораздо меньше проблем, связанных с отображением иконок в современных браузерах.

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

Как оптимизировать загрузку шрифтов

Но для начала стоит убедиться, что все шрифты, которые мы используем, будут предварительно загружены.

Обычно шрифты загружаются в режиме lazy-loading, что означает, что браузер загружает HTML, JavaScript и CSS, прежде чем начать загружать и обрабатывать шрифт, который объявлен в CSS.

Чтобы это изменить, мы можем использовать link rel="preload" в теге <head>, чтобы браузер начал загружать шрифт до того, как обнаружит его обычным способом. Это означает, что шрифт загрузится гораздо быстрее.

<link rel="preload" href="/CoolFont.woff" as="font" type="font/woff2" crossorigin />

Другой вариант – указание в правиле CSS @font-face свойства font-display со значением optional.

Когда мы используем значение optional, то при загрузке шрифта более трёх секунд, браузер просто откажется от попытки загрузить его. Отображаться будет только то, что успело загрузиться. Больше никаких изменений не будет, а значит не будет и сдвигов макета. То что уже отображается, то и будет отображаться. Так что больше никаких изменений, никакого смещения макета.

Также нужно убедиться, что мы не используем подмену отображения шрифтов. Замена отображения шрифта говорит браузеру показать любой существующий запасной шрифт, а затем поменять его на нужный, как только он закончит загрузку, независимо от того, сколько времени это займет.

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

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

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

Это всё, чем поделился Патрик Кеттнер в видео. Если у вас есть вопросы, вы можете задать их под видео в YouTube. Или посмотрите видео в оригинале:

Ранее Патрик рассказывал о том, как улучшить метрику FID в Core Web Vitals

В наших новостях всегда есть немного больше, чем у других. Подпишитесь на канал в Телеграм и читайте интересные новости первыми.

Подписаться