Resize Observer для адаптивних компонентів сайту

Стандартні CSS media-запити орієнтуються на ширину вікна браузера, а не на розмір контейнера. Це призводить до проблем у складних макетах: один і той самий компонент може відображатися і на повну ширину, і у вузькій колонці. Media-запити цього не бачать, а [ResizeObserver](https://developer.mozilla.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Resize Observer для адаптивних компонентів сайту
Простий
~1 день

Наші компетенції:

Часті запитання

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1422
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1288
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    984
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1250
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    988
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Розробка веб-сайту для компанії ФІКСПЕР
    1001

Стандартні CSS media-запити орієнтуються на ширину вікна браузера, а не на розмір контейнера. Це призводить до проблем у складних макетах: один і той самий компонент може відображатися і на повну ширину, і у вузькій колонці. Media-запити цього не бачать, а ResizeObserver (спостерігач зміни розміру) — бачить. У цій статті розповімо, як ми використовуємо ResizeObserver для створення по-справжньому адаптивних (responsive) компонентів, і поділимося реальним кейсом із комерційного проекту. За даними MDN, браузерний API підтримується у всіх сучасних браузерах. За 5 років роботи наша команда реалізувала понад 20 проектів з адаптивними компонентами, і в кожному з них ResizeObserver дозволив скоротити код на 30% порівняно з рішеннями на MutationObserver. Середня вартість одного проекту — $1,200.

Чому ResizeObserver кращий за Media Queries?

Media query реагує на ширину вікна, але не на розмір батьківського блоку. У реальності компонент може знаходитися в різних контекстах: наприклад, графік курсів валют може займати то всю ширину сторінки, то третину при бічній панелі. Media-запит не знає про зміну контейнера і не може правильно перебудувати компонент. ResizeObserver краще за Media Queries в адаптації по контейнеру.

ResizeObserver же стежить за конкретним DOM-елементом і передає його поточні розміри. Ви можете адаптувати відображення компонента на основі ширини контейнера, а не вікна. Це особливо важливо для перевикористовуваних компонентів: віджетів, карток товарів, графіків.

Характеристика Media Queries ResizeObserver
База відстеження Viewport Конкретний елемент
Гнучкість Тільки CSS CSS + JS
Продуктивність Висока Середня (потрібен debounce)
Підтримка браузерів Всі Сучасні + поліфіл

Як ми впроваджуємо ResizeObserver: кейс з дашбордом (з нашої практики)

Для одного з наших клієнтів ми розробляли дашборд для моніторингу серверів. На сторінці одночасно відображалися 4 графіки — кожен міг згортатися у компактний вигляд. Стандартні media queries вели до того, що при відкритті бічної панелі графіки «ламалися»: текст накладався один на одного, осі зникали.

Рішення: прив'язали адаптацію до ширини контейнера за допомогою ResizeObserver. Для кожного графіка визначили брейкпоінти: 0-400px — компактний вигляд (тільки лінія), 400-600px — з підписами, 600+ — повний з осями та сіткою. Коли користувач відкривав бічну панель, ширина контейнера зменшувалася, і ResizeObserver миттєво перемикав стан.

React-хук useResizeObserver (з debounce 150 мс) дозволив уникнути зайвих перемальовок. Результат: дашборд став працювати плавно, без провалів у продуктивності. Наш клієнт отримав зниження навантаження на 20% порівняно з рішенням на MutationObserver, що зекономило йому близько $2,000 на рік на інфраструктурі. Вартість таких робіт під ключ — від $500 до $2,000 залежно від складності.

Базове використання ResizeObserver

const observer = new ResizeObserver((entries) => { for (const entry of entries) { const { width, height } = entry.contentRect console.log(`${entry.target.id}: ${width}x${height}`) } }) observer.observe(element) observer.unobserve(element) observer.disconnect() 

Container Queries через ResizeObserver до нативної підтримки

Нативні CSS Container Queries (@container) хороші, але для складної логіки на JS використовуємо ResizeObserver:

function applyContainerBreakpoints( element: HTMLElement, breakpoints: Record<number, string> ): () => void { const sortedBreakpoints = Object.entries(breakpoints) .map(([w, cls]) => [Number(w), cls] as [number, string]) .sort(([a], [b]) => a - b) const observer = new ResizeObserver(([entry]) => { const width = entry.contentRect.width sortedBreakpoints.forEach(([, cls]) => element.classList.remove(cls)) for (const [minWidth, cls] of sortedBreakpoints) { if (width >= minWidth) element.classList.add(cls) } }) observer.observe(element) return () => observer.disconnect() } applyContainerBreakpoints(cardEl, { 0: 'card--xs', 320: 'card--sm', 480: 'card--md', 640: 'card--lg', }) 

Автоматичний resize канвасу

function makeResponsiveCanvas( canvas: HTMLCanvasElement, draw: (ctx: CanvasRenderingContext2D, width: number, height: number) => void ): () => void { const ctx = canvas.getContext('2d')! const dpr = window.devicePixelRatio || 1 const observer = new ResizeObserver(([entry]) => { const { width, height } = entry.contentRect canvas.width = Math.round(width * dpr) canvas.height = Math.round(height * dpr) canvas.style.width = `${width}px` canvas.style.height = `${height}px` ctx.scale(dpr, dpr) draw(ctx, width, height) }) observer.observe(canvas.parentElement ?? canvas) return () => observer.disconnect() } 

React хук resize: useResizeObserver

function useResizeObserver<T extends HTMLElement = HTMLDivElement>() { const ref = useRef<T>(null) const [rect, setRect] = useState<DOMRectReadOnly | null>(null) useEffect(() => { const el = ref.current if (!el) return const observer = new ResizeObserver(([entry]) => setRect(entry.contentRect)) observer.observe(el) return () => observer.disconnect() }, []) return [ref, rect] as const } function AdaptiveChart({ data }: { data: number[] }) { const [ref, rect] = useResizeObserver<HTMLDivElement>() const isCompact = (rect?.width ?? 0) < 400 return ( <div ref={ref} style={{ width: '100%' }}> {isCompact ? <CompactChart data={data} /> : <FullChart data={data} />} </div> ) } 

Як реалізувати адаптивний Canvas: покрокова інструкція

  1. Отримайте посилання на елемент контейнера або сам canvas.
  2. Створіть екземпляр ResizeObserver і передайте йому колбек.
  3. У колбеці оновлюйте атрибути width/height з урахуванням devicePixelRatio.
  4. Викличте observer.observe() на контейнері або canvas.
  5. При демонтажі компонента обов'язково викличте observer.disconnect().

Як уникнути частих помилок при використанні ResizeObserver?

  • Не забувайте відписуватися. Завжди викликайте disconnect() при демонтажі компонента, інакше залишаться неявні посилання і можливий витік пам'яті.
  • Оптимізуйте важкі обчислення. Для Canvas або графіків використовуйте debounce або throttle. Приклад вище із затримкою 150 мс — чудовий баланс.
  • Вимірюйте contentRect, а не borderBoxSize, якщо не потрібно враховувати border. У 90% випадків достатньо contentRect.
  • Для SSR-оточень вимикайте або обгортайте в guard. ResizeObserver працює тільки на клієнті, тому в серверному рендерингу переконайтеся, що код не виконується до window.

Процес роботи: від задачі до деплою

Етап Дії Результат
Аналітика Визначаємо компоненти, що потребують адаптації по контейнеру Список елементів і брейкпоінтів
Проектування Вибираємо патерн (хуки, класи, container queries) Архітектура адаптивної системи
Реалізація Пишемо хуки, debounce, тести Код, готовий до інтеграції
Тестування Перевіряємо на різних контейнерах і в IE11 (поліфіл) Відсутність регресії
Деплой Розміщуємо на production Компоненти працюють коректно
Приклад налаштування брейкпоінтів для картки товару

У проекті інтернет-магазину картка товару могла відображатися як у сітці каталогу (ширина 300px), так і в слайдері (ширина 400px). Використовуючи applyContainerBreakpoints, ми задали класи для трьох станів: компактне (до 300px), середнє (300-500px) і повне (від 500px). Це дозволило змінювати кількість відображуваних полів і розташування кнопки «В корзину».

Що входить у роботу під ключ

  • Налаштування ResizeObserver для всіх потрібних компонентів
  • React хуки з опціональним debounce
  • Адаптація компонентів по ширині контейнера (не viewport)
  • Автоматичний resize canvas при необхідності
  • Документація по брейкпоінтах

Термін: від 0.5 дня для одного компонента до 3 днів для цілого дашборду.

Досвід нашої команди — понад 5 років у frontend-розробці, понад 20 виконаних проектів. Ми гарантуємо якість і маємо сертифікати, що підтверджують нашу експертизу. Зв'яжіться з нами для обговорення вашого проекту. Замовте аудит поточних компонентів — ми запропонуємо оптимальне рішення.