Жалоба звучала так: «на айфоне обновляю главную где-нибудь в середине, и она маленькими рывками ползёт вниз». Мы прогнали замер сдвигов вёрстки (CLS) в Chrome с медленной сетью и замедленным процессором — ноль во всех прогонах. А страница при этом действительно ползла.

Это первая часть цикла «Вёрстка без сюрпризов» — о багах, которые видит только посетитель.

Почему Chrome показывает ноль

У Chrome есть привязка прокрутки (scroll anchoring). Если содержимое выше видимой области меняет высоту, браузер сдвигает прокрутку так, чтобы то, на что смотрит человек, осталось на месте. Сдвига на экране нет — и в метрику сдвигов он не записывается.

У Safari привязки прокрутки нет. Блок выше экрана вырос на 30 пикселей — всё, что видит человек, уехало на 30 пикселей вниз. Поэтому жалоба приходит только с айфонов, а все замеры CLS показывают ноль.

CLS в Chrome равен нулю — не значит, что ничего не двигается. Это значит, что Chrome компенсировал сдвиг сам.

Что мерить вместо CLS

  • высоту документа во времени — document.documentElement.scrollHeight каждые 100–150 мс от загрузки до тишины;
  • абсолютное положение конкретного элемента до и после события: getBoundingClientRect().top + window.scrollY;
  • в дробных пикселях — toFixed(2). Разница в 0,14 пикселя без дробей не видна вовсе, а на устройстве она превращается в 2 пикселя.
const log = [];
const el = document.querySelector('.productLine h2');
const t0 = performance.now();
const timer = setInterval(() => {
  log.push({
    t: Math.round(performance.now() - t0),
    doc: document.documentElement.scrollHeight,
    y: (el.getBoundingClientRect().top + scrollY).toFixed(2),
  });
}, 120);
setTimeout(() => { clearInterval(timer); console.table(log); }, 8000);

Опрос — setInterval, а не цикл на каждый кадр. Чтение геометрии на каждом кадре заставляет браузер пересчитывать раскладку, и на тяжёлой странице сам замер начинает тормозить её на секунды.

Что нашли этим замером

Три причины, все три — сдвиги выше экрана:

  1. подмена шрифта — 142 пикселя на страницу. Пока грузится веб-шрифт, текст рисуется системным, а он уже: часть названий помещалась в две строки вместо трёх. Лечение — запасной шрифт с подогнанными метриками, подробно в 19-й части цикла про PageSpeed;
  2. заглушки ajax-блоков меньше самих блоков — 272 пикселя;
  3. aspect-ratio и округление ширины слайдера — 2 пикселя на каждой из двадцати строк. Об этом завтра.

Заглушки: одна на всех — значит неверная у всех

Блоки, которые грузятся по мере прокрутки, сначала представлены заглушкой фиксированной высоты. У нас она была общей — 590 пикселей. Реальные высоты блоков на телефоне:

Ширина экрана360390430560
блок дилеров7087678461102
подвал640685746945
резерв590590590590

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

Важная деталь: содержимое этих блоков свёрстано в vw, поэтому их высота строго пропорциональна ширине. 708 / 360 = 767 / 390 = 846 / 430 = 1102 / 560 — ровно 196,7vw. Значит, и резерв надо задавать в vw и для каждого блока свой:

.asyncBlock[data-block="dealers"] { min-height: 196.7vw; }
@media (min-width: 821px) {
  .asyncBlock[data-block="dealers"] { min-height: 837px; }
}

Проверка: прогнать реальные высоты блока по 6–8 ширинам и сверить с резервом. Если высота пропорциональна ширине — резерв в vw, если нет — несколько ступеней по ширинам.

Что проверили и что не подтвердилось

  • «пересчёт слайдера при схлопывании адресной строки айфона» — замер изменения только высоты окна дал ноль;
  • кадры галерей без размеров — у них есть width и height, загрузка не меняет рамку;
  • сокращение DOM и content-visibility к этой задаче отношения не имеют.

Страница «дёргается» на айфоне, а в Chrome всё ровно?

Пришлите адрес — найду, что меняет высоту выше экрана, замером, а не на глаз.

· полезно?
АЧ
Алексей Чикун веб-разработчик, команда TRIOHEAD

Делаю сайты и сервисы целиком: от вёрстки до сервера. Пишу о том, что проверил на своих проектах.

Комментарии

// загружаю комментарии…

Комментарии публикуются после проверки. Ссылки и HTML не принимаются.