Реализация Resize Observer для адаптивных компонентов сайта

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

Разработка и обслуживание любых видов сайтов:

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

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

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Реализация Resize Observer для адаптивных компонентов сайта
Простая
~1 рабочий день
Часто задаваемые вопросы

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

Этапы разработки

Последние работы

  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    874
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    851

Реализация Resize Observer для адаптивных компонентов сайта

ResizeObserver следит за изменением размеров конкретного DOM-элемента. Не viewport, а именно элемента. Это ключевое отличие от CSS media queries — здесь адаптивность привязана к контейнеру компонента, а не к ширине экрана.

Классический случай: компонент графика занимает то всю ширину страницы, то треть. CSS media query этого не видит. ResizeObserver — видит.

Базовое использование

const observer = new ResizeObserver((entries) => {
  for (const entry of entries) {
    // entry.contentRect — размеры контентной области
    // entry.borderBoxSize — размеры с учётом border
    // entry.contentBoxSize — без padding/border
    // entry.devicePixelContentBoxSize — в физических пикселях

    const { width, height } = entry.contentRect
    console.log(`${entry.target.id}: ${width}x${height}`)
  }
})

observer.observe(element)
observer.unobserve(element)
observer.disconnect()

Container Queries через JavaScript до нативной поддержки

Нативные CSS Container Queries (@container) поддерживаются в современных браузерах, но для старых или для сложной логики — 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)

    // CSS-размер остаётся прежним
    canvas.style.width = `${width}px`
    canvas.style.height = `${height}px`

    // Масштабируем контекст для retina
    ctx.scale(dpr, dpr)

    draw(ctx, width, height)
  })

  observer.observe(canvas.parentElement ?? canvas)
  return () => observer.disconnect()
}

React-хук

function useResizeObserver<T extends HTMLElement = HTMLDivElement>(): [
  RefObject<T | null>,
  DOMRectReadOnly | null,
] {
  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]
}

// Адаптивный компонент на основе хука:
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} width={rect?.width} />
      ) : (
        <FullChart data={data} width={rect?.width} height={rect?.height} />
      )}
    </div>
  )
}

Debounce для частых изменений

ResizeObserver срабатывает при каждом изменении размера — при ресайзе окна это может быть несколько раз в секунду. Для тяжёлых вычислений нужен debounce:

function useResizeObserverDebounced<T extends HTMLElement>(
  delay = 150
): [RefObject<T | null>, DOMRectReadOnly | null] {
  const ref = useRef<T>(null)
  const [rect, setRect] = useState<DOMRectReadOnly | null>(null)
  const timerRef = useRef<ReturnType<typeof setTimeout>>()

  useEffect(() => {
    const el = ref.current
    if (!el) return

    const observer = new ResizeObserver(([entry]) => {
      clearTimeout(timerRef.current)
      timerRef.current = setTimeout(() => setRect(entry.contentRect), delay)
    })

    observer.observe(el)
    return () => {
      observer.disconnect()
      clearTimeout(timerRef.current)
    }
  }, [delay])

  return [ref, rect]
}

Что входит в работу

Настройка ResizeObserver для нужных компонентов, React-хуки с debounce, адаптация компонентов по ширине контейнера (а не viewport), автоматический resize canvas при необходимости.

Срок: 0.5 дня.