Пропуск рендерингу поза екраном за допомогою content-visibility та contain-intrinsic-size
Дізнайтеся, як параметр content-visibility: auto зменшує витрати на форматування на довгих сторінках, які генеруються сервером, чому обов’язково використовувати параметр contain-intrinsic-size, та як це порівнюється з віртуалізацією.
Довгі сторінки, наповнені повторюваними елементами — такими як списки товарів, фіди, архіви та великі таблиці, — часто працюють повільно навіть тоді, коли обсяг JavaScript-коду мінімальний. Причиною зазвичай є те, що браузер обчислює стилі та макет для кожного елемента на сторінці, включаючи сотні карток, до яких відвідувач ще не прокрутився та можливо ніколи не дістанеться. У цьому посібнику показано, як два властивості CSS — content-visibility та contain-intrinsic-size — дозволяють браузеру відкласти цю роботу, як ця зміна впливає на показники Core Web Vitals та процес індексування, а також де цей підхід не спрацьовує або є неправильним інструментом.
Типовий випадок: довгий список, який працює повільно без очевидних причин
Уявіть собі сторінку категорії «Переглянути всі товари» для інтернет-магазину. Приблизно 600 карток товарів, кожна з яких має зображення, назву, ціну та оцінку, обробляються на сервері та формують одну довгу сторінку. Немає пагінації, немає функції безкінечного прокручування та немає віртуалізації. Компанія задоволена таким підходом: єдиний URL, кожен товар доступний для перегляду, все можна знайти за допомогою функції пошуку в сторінці браузера.
На ноутбуку середнього класу сторінка готова до взаємодії лише через майже чотири секунди. Першим підозрюваним є JavaScript: занадто великий пакет коду, некоректно працюючий useEffect, компоненти, які постійно переробляються у циклі. Ретельний аналіз пакету не виявив нічого, що могло б пояснити затримку.
Панель продуктивності в DevTools розповідає іншу історію. Більша частина часу основної смуги використовується на формування макету, і це відбувається ще до того, як скрипти встигають вплинути на роботу сторінки.
Чому контент поза екраном все одно коштує вам грошей
Обробка не є однією операцією. Браузер спочатку визначає стилі для кожного елемента, потім розраховує макет, щоб визначити розмір та положення кожної області, і лише після цього наносить пікселі. Нанесення пікселів в основному обмежується тим, що знаходиться всередині або близько до вікна перегляду, проте процеси визначення стилів та розрахунку макету виконуються для всього документа, включаючи контент, який знаходиться далеко внизу. До того часу, як браузер вирішить, що саме нанести, дорога геометрична обробка вже завершилася.
У прикладі магазину це означає, що всі 600 карток — кожна з полем для зображення, заголовком упаковки, ціною та рядком оцінок — проходять обробку стилю та макетування, перш ніж покупець побачить першу з них. Покупець бачить, можливо, вісім товарів. Інші 592 поки що не корисні, але браузер не може знати, чи буде відвідувач коли-небудь прокручуватися вниз, тому за замовчуванням він розглядає їх усі як контент, який має бути готовий відразу. Саме це марнотратство варто усунути. Якщо ви хочете ознайомитися з тим, як ці етапи обробки впливають на витрати, перегляньте наш огляд того, скільки коштує для браузера процес рефлоу, переробки зображення та композиції.
Рішення: дві декларації для елемента, який повторюється
Застосуйте обидві властивості до елемента, який повторюється, тобто до картки товару. Перша повідомляє браузеру, що він може пропустити відображення вмісту картки, коли вона знаходиться далеко від області перегляду. Друга визначає розмір місця для заміни для карток, реальний розмір яких ще невідомий.
.product-card {
content-visibility: auto;
contain-intrinsic-size: auto 340px;
}
За допомогою content-visibility: auto картка, яка не знаходиться близько до області перегляду, залишається в DOM та у дереві доступності, і функція find-in-page все одно знаходить її текст. Змінюється лише те, що браузер не витрачає ресурси на стилі, макетування та відображення її вмісту, поки картка не наблизиться до екрана. Фактично ця властивість застосовує обмеження щодо макетування, стилів та відображення до елемента, що дозволяє браузеру розглядати цю піддеревину як окрему та безпечно її пропускати.
Наскільки великими можуть бути переваги
У демонстрації, опублікованій на web.dev, Google взяв довгу сторінку, розділену на секції, та скоротив час її відображення з 232 мс до 30 мс, що є приблизно у сім разів кращим результатом. Слід пам’ятати, що це спеціально створена демонстраційна сторінка, а не продакшн-сайт. Для справжньої сторінки товару, як описано вище, більш реалістичним є покращення приблизно в 2 рази, що все одно може стати різницею між сторінкою, яка все ще завантажується, та сторінкою, яка вже готова до використання з першого моменту відображення.
Екстремальні приклади демонструють максимальні можливості. У промові на Chrome Dev Summit цей підхід було застосовано до величезної односторінкової HTML-специфікації з понад 270 000 вузлами DOM, і час формування макету знизився з приблизно 50 секунд до близько 400 мілісекунд. Таких сторінок мало, але це ілюструє, скільки зусиль вкладається у контент, який ніхто не бачить.
Важливо бути точним щодо того, що відбувається. CSS не прискорює процесор. Ви повідомляєте браузеру, що велика частина роботи не потрібна зараз. Коли на сторінці є 600 карток, а у вікні перегляду видно лише вісім, обробка кожної картки заздалегідь рідко є найкращим використанням основної смуги виконання.
Чому параметр contain-intrinsic-size не є необов’язковим
Якщо використати лише content-visibility: auto, ползунок скроллу починає різко змінювати положення під час прокручування, що легко можна сплутати з якоюсь іншою проблемою.
Причина проста. У картки, розмітка якої була проігнорована, немає відомої висоти, тому браузер встановлює її розмір, ніби вона порожня. При сотнях проігнорованих карток загальна висота документа сильно спотворюється, і ползунок скроллу відображає цю неправильну висоту. Коли картки потрапляють у поле зору та отримують свої справжні розміри, документ збільшується, і ползунок скроллу зміщується.
contain-intrinsic-size визначає розмір, який слід припускати під час пропуску елемента. Значення 340px у прикладі — це орієнтовна величина для карток, які ще не були відрендеровані. Точність не є обов’язковою; достатня апроксимація запобігає помітному руху скролбара.
Що додає ключове слово auto
Ключове слово auto легко проігнорувати, проте воно має велике значення. За допомогою auto, як тільки картка була відрендерована, браузер фіксує її фактичний розмір. Якщо пізніше картка виходить за межі видимої області та знову пропускається, браузер використовує саме цей збережений розмір замість вашої оцінки. Без auto кожна картка щоразу, коли виходить за межі видимості, повертається до фіксованої оцінки розміру.
Картки на екрані завжди відображаються у своїй справжній висоті незалежно від обставин. Різниця проявляється у контенті з змінною висотою: довга назва товару, яка розтягується на окремий рядок, або індикатор знижки, який додає ще один рядок. Без параметра auto ці картки повертаються до орієнтовних розмірів, коли виходять за межі видимої області та змінюється положення прокрутки. Завжди використовуйте auto.
Підтримка браузерами та поступове покращення
Підтримка більше не є прерогативою лише Chrome. Chrome підтримує content-visibility з версії 85 (2020 р.), Firefox — з версії 125, а Safari — з версії 18. На момент написання цього тексту статус цієї функції вказаний як «Baseline Newly Available», досягнутий 15 вересня 2025 року, що означає її працездатність у всіх трьох основних браузерах; перевірте актуальні дані про сумісність, якщо ви підтримуєте старіші версії.
Браузери, які не розуміють цього атрибута, просто ігнорують його та відображають усе так, як завжди. Це робить його засобом поступового покращення без будь-яких недоліків для старіших клієнтів.
Як це пов’язано з Core Web Vitals та SEO
Мотивацією є продуктивність, але сторінки категорій саме той тип URL-адрес, які команди з пошуку уважно стежать. Вони є публічними, орієнтовані на цінні запити, і Google вимірює їхню продуктивність для реальних користувачів.
Робота з макетом та відображенням впливає на два з трьох показників Core Web Vitals, які Google використовує як сигнали для ранжування:
- Interaction to Next Paint (INP) безпосередньо покращується. Пропуск обробки елементів, які знаходяться поза екраном, звільняє основну смугу виконання, тож натискання отримують відповідь швидше.
Багато сторінок, що не належать до категорії комерційних, мають таку структуру — від документації та новинних лент до архівів блог-постів, довгих тем обговорень та статей з кількома розділами. Там, де багато схожих елементів розташовані вертикально та більшість з них спочатку знаходяться поза екраном, а сторінка є публічною, ця зміна впливає на показники, які вимірює Google.
Будьте обережні з обіцянками покращення рангування. Спостереження за одним сайтом протягом кількох тижнів мало що доводить, а рангування залежить від набагато більшої кількості показників. Те, чого можна розумно очікувати, — це зміни у правильному напрямку в даних, отриманих від реальних користувачів, наприклад у PageSpeed Insights, коли накопичиться достатня кількість зразків.
Перевага не обмежується лише публічними сторінками. Панелі керування, таблиці адміністратора та внутрішні інструменти також стикаються з тим самим обмеженням у витратах на відображення 600 рядків. Після входу немає додаткової переваги у пошуку, але покращення користувацького досвіду залишається таким самим завдяки тому ж CSS.
Чи приховує пропуск відображення контент від пошукових ботів?
Це перше питання, яке поставить уважний фахівець з SEO, і воно є цілком логічним, адже існує багато схем зволіканого завантаження, які роблять контент невидимим для ботів. Ключова відмінність полягає у тому, що content-visibility: auto — це оптимізація відображення, а не зміна видимості. Контент існує в HTML та DOM з моменту завантаження сторінки; лише його розташування та відображення відкладаються до моменту, коли елемент наближається до вікна перегляду.
Googlebot не прокручує сторінку так, як це робить людина. Натомість він використовує надзвичайно високий простір перегляду для відтворення контенту, а потім аналізує отриманий DOM. Картки, які пропускаються, але все одно є на сторінці, є частиною цього DOM, тому кожна назва товару та ціна індексуються так само, як і будь-який інший контент.
На відміну від цього, у старішому підході контент додавався лише після виникнення події прокрутки. Пошукові боти не генерують подій прокрутки, тому такий контент справді міг би бути відсутнім у індексі. Параметр content-visibility не може спричинити цю проблему, оскільки нічого не додається пізніше; усе є з самого початку.
Одна застереження не стосується самої власності. Якщо сторінка формує свій вміст за допомогою JavaScript на клієнтській стороні, виникає окрема проблема з SEO. Googlebot дійсно виконує JavaScript, але його обробка відбувається у черзі, що робить процес повільнішим та менш надійним, крім того, багато інших сканерів погано справляються з JavaScript. Для публічного вмісту необхідно обробляти HTML на сервері та додати атрибут content-visibility у CSS. Це поєднання забезпечує можливість сканування вмісту та кращі показники його доступності.
Порівняння з безкінечним прокручуванням, віртуалізацією та користувацькими спостерігачами
Безкінечне прокручування, API з пагінацією та віртуалізація — усі вони спрямовані на вирішення однієї й тієї ж проблеми повільних довгих сторінок, тому варто їх чесно порівняти.
Завантаження під час прокручування та API з пагінацією
Завантаження додаткових елементів під час прокручування користувачем допомагає вирішити проблему на рівні даних. Це правильний вибір, коли набір даних справді величезний та не повинен надсилатися до браузера в повному обсязі.
Це має свої витрати. Потрібні зміни до API, обробка станів завантаження, відстеження прокручування та стану того, що вже було завантажено. Також змінюється користувацький досвід: функція пошуку в сторінці не може знаходити елементи, які ще не завантажені, а досягнення кінця списку стає складним. На публічній сторінці категорії існує додаткове навантаження на SEO, оскільки продукти, які з’являються лише після прокручування, недоступні для пошукових роботів; потрібні URL-адреси з пагінацією та додатковий маркап, щоб вони залишалися індексованими. Для 600 карток, які вже є в HTML, це означає необхідність переписування коду та додаткової роботи з SEO для вирішення проблеми, яка насправді є проблемою відображення.
Бібліотеки віртуалізації
Віртуалізація за допомогою бібліотек на кшталт react-window чи TanStack Virtual зберігає весь набір даних у пам’яті, видаляючи елементи DOM того, що знаходиться поза видимим вікном. Цей підхід ефективний, і при дуже великих обсягах даних це кращий вибір, як буде пояснено нижче.
Ціна пов’язана з вимогами до JavaScript, необхідністю перепису компонентів та складним керуванням рядками з мінливою висотою. Оскільки видалені елементи зовсім не знаходяться у DOM, функція find-in-page не може їх бачити, програми для читання екрана мають з тими проблеми, а пошукові боти на публічних сторінках їх ігнорують.
Підхід з використанням IntersectionObserver
Відображення елементів вручну, коли IntersectionObserver повідомляє про їх видимість, означає повторне створення того, що вже робить content-visibility: auto у основному потоці JavaScript, а також повернення проблем з фіксацією позиції під час прокрутки, які браузер вже вирішив вбудовано. Сьогодні немає значних причин писати код таким чином.
Вибір між ними
Справжньою перевагою content-visibility є не те, що він кращий за ці методи, а те, що він значно дешевший. Це просто одна властивість CSS: без JavaScript, без змін API, без переписування коду, і контент залишається в DOM для пошуку, технологій допомоги користувачам та функції пошуку в межах сторінки.
Компроміс має бути чітко сформульований. content-visibility економить ресурси на відображенні, а не пам’ять. Кожен елемент DOM все одно існує. При 600 картках це майже незначно. Але при 50 000 або 100 000 елементах розмір DOM стає проблемою сам по собі, і тут вже доцільна віртуалізація. Перш ніж обирати інструмент, визначте, яка з цих двох проблем у вас є — витрати на відображення чи розмір DOM.
Де він працює та де може призвести до проблем
Це не та властивість, яку можна використовувати всюди. Вона найбільш ефективна там, де одна й та сама структура повторюється багато разів, наприклад:
- картки в каталозі чи списку
Будь-що, що є одним із багатьох схожих блоків, розташованих вертикально, причому більшість з них знаходяться поза екраном під час завантаження, є гарним кандидатом для тестування.
Вимірювання пропущеного контенту дає неправильні числа
Ця властивість конфліктує з кодом, який потребує точної геометрії піддрозділу до того, як цей піддрозділ буде відрендерований. Типовим прикладом є отримання висоти внутрішнього елемента рядка, який все ще знаходиться поза екраном.
const height = row
.querySelector('.details')
.getBoundingClientRect()
.height;
Вимірювання внутрішнього контенту пропущеного елемента за допомогою getBoundingClientRect() до того, як він був відрендерований, дає значення нуля або інші некоректні дані. Власна рамка елемента вказує розмір місця для заміни за допомогою contain-intrinsic-size, який також може не відповідати реальності. Такий код є поширеним у реальних інтерфейсах, наприклад, коли ви:
- розміщуєте підказку поруч із тригером
- визначаєте початкові та кінцеві значення для анімації
- вирішуєте, де має відкриватися меню з падаючим списком
- визначаєте розмір рядків у віртуальному списку
- реалізуєте логіку фіксованого розташування
- вирівнюєте один компонент відносно іншого
Якщо точна геометрія має значення ще до того, як контент стане видимим, ретельно протестуйте код перед додаванням цієї властивості.
Інші погані варіанти
- Зафіксовані заголовки, а також макети, у яких обчислення працюють лише тоді, коли всі дочірні елементи мають реальну геометрію в один і той самий момент.
- Будь-що, що знаходиться над видимою частиною сторінки. Цей контент мусить відображатися негайно, тож ця властивість не приносить жодних переваг та потребує додаткового обліку.
- Елементи, візуальні ефекти яких виходять за межі їхньої області. Оскільки ця властивість забезпечує обмеження зони фарбування, контент, що виходить за межі, такий як великі тіні чи поп-апи, розташовані всередині картки, може бути обрізаний на краю картки.
Два менш відомі нюанси
Прихована вартість
content-visibility: hidden ухиляється від відображення елемента, подібно до display: none, але браузер зберігає стан його відображення у кеші. Повторне відображення є значно дешевшим, ніж показати елемент з display: none, оскільки не потрібно починати всю роботу з нуля. Це робить його корисним для вкладок, меню поза екраном та віртуальних скролерів. На відміну від auto, контент, який знаходиться у стані hidden, недоступний для пошуку всередині сторінки під час приховання.
Реагування на зміни стану ухилення
Кожного разу, коли елемент із content-visibility: auto переходить між станами прихованого та відображеного, браузер надсилає подію contentvisibilityautostatechange. Слухаючи цю подію, ви можете призупинити ресурсомісткі скрипти, такі як малювання на canvas, для контенту, який браузер все одно не відображає.
Перевірка ефекту на власних сторінках
Короткий експеримент чітко показує різницю. Створіть тестову сторінку з приблизно 1 000 картками та перемикачем, який додає або видаляє content-visibility: auto. Відкрийте DevTools, перейдіть до панелі Performance, записайте процес завантаження без увімкненого перемикача, потім знову записайте його з увімкненим перемикачем та порівняйте фіолетові блоки Layout у обох записах.
Потім застосуйте ту саму перевірку до вашої найдовшої сторінки у продакшені. Зробіть запис процесу її завантаження. Коли частина Layout займає перевагу, а інтерактивність з’являється пізно, додавання content-visibility: auto до повторюваних елементів часто є найдешевшим реальним покращенням: одна властивість, без переписування коду та без міграції у фреймворк.
Основні висновки
- Браузери обробляють стиль та макет для всього документа;
content-visibility: autoдозволяє їм відкласти цю роботу для елементів, які знаходяться далеко від області перегляду, не видаляючи нічого з DOM. - Завжди поєднуйте його з
contain-intrinsic-size: auto <estimate>у тій самій зміні, інакше ползунок буде різко змінювати положення. - Контент залишається індексованим, пошуковим та доступним, що відрізняє його від методів завантаження контенту під час прокручування та віртуалізації.
- Це економить час на відрендерування, а не пам’ять; коли сам DOM стає занадто великим, правильним рішенням є віртуалізація.
- Уникайте використання цього параметра над основним контентом, на елементах, геометрію яких ви вимірюєте перед відображенням, та на компонентах, які малюються поза межами своєї області.