Кастомный scroller для модальных окон

Headroom.js — это библиотека, предназначенная для управления поведением элементов при скролле страницы. В классическом варианте она чаще используется для шапок сайта, скрывая их при прокрутке вниз и показывая при прокрутке вверх. Однако принципы её работы можно эффективно применять и для кастомных скроллеров внутри модальных окон, где стандартный window.scroll не применяется, а прокрутка происходит в отдельном контейнере.

Инициализация Headroom для модального контейнера

Вместо привязки к window необходимо указать конкретный контейнер модального окна как scrolling element. Например:

const modal = document.querySelector('.modal');
const header = modal.querySelector('.modal-header');

const headroom = new Headroom(header, {
  scroller: modal, // контейнер, за которым следим
  tolerance: {
    up: 5,
    down: 10
  },
  offset: 50,
  classes: {
    initial: 'headroom',
    pinned: 'headroom--pinned',
    unpinned: 'headroom--unpinned',
    top: 'headroom--top',
    notTop: 'headroom--not-top'
  }
});

headroom.init();

Ключевые моменты:

  • scroller — элемент, у которого происходит прокрутка. По умолчанию Headroom слушает window.
  • tolerance — порог чувствительности для скролла вверх (up) и вниз (down). Позволяет избежать “дребезга” при мелких движениях.
  • offset — расстояние в пикселях от начала скролла до первой реакции Headroom. Полезно, когда верхняя часть модального окна может содержать пустое пространство или padding.
  • classes — настраиваемые CSS-классы, обеспечивающие плавные анимации при изменении состояния.

Обработка динамически меняющегося контента

Модальные окна часто содержат динамический контент, который может изменять высоту контейнера. Для корректной работы Headroom нужно учитывать пересчет размеров:

const resizeObserver = new ResizeObserver(() => {
  headroom.destroy();
  headroom.init();
});

resizeObserver.observe(modal);
  • ResizeObserver следит за изменением размеров контейнера.
  • При больших изменениях контента полезно переинициализировать Headroom, чтобы корректно отслеживать верхнюю и нижнюю границы скролла.

Настройка анимаций и плавности

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

.headroom {
  transition: transform 0.3s ease-in-out;
}

.headroom--pinned {
  transform: translateY(0);
}

.headroom--unpinned {
  transform: translateY(-100%);
}
  • transform работает лучше, чем top или margin, так как использует аппаратное ускорение.
  • transition определяет скорость анимации и кривую ускорения, что особенно важно для модальных окон с ограниченной высотой.

Интеграция с событиями модального окна

Headroom должен корректно реагировать на открытие и закрытие модального окна. Это можно реализовать через события:

modal.addEventListener('open', () => {
  headroom.init();
});

modal.addEventListener('close', () => {
  headroom.destroy();
});
  • init и destroy гарантируют, что библиотека не будет реагировать на скролл, когда модальное окно скрыто.
  • Это уменьшает нагрузку на DOM и предотвращает непредсказуемое поведение при динамическом контенте.

Работа с nested scroll контейнерами

Если внутри модального окна есть внутренние скролл-области (например, список сообщений), Headroom можно применить к каждой области отдельно:

const innerScroll = modal.querySelector('.messages');
const innerHeadroom = new Headroom(innerScroll.querySelector('.inner-header'), { scroller: innerScroll });
innerHeadroom.init();
  • Это позволяет управлять видимостью заголовков отдельных секций независимо от основного модального контейнера.
  • Настройка tolerance и offset может отличаться от внешнего скролла для большей отзывчивости.

Оптимизация производительности

Для модальных окон с большим количеством контента следует учитывать частоту обновления скролл-событий:

const throttledScroll = _.throttle(() => headroom.update(), 50);
modal.addEventListener('scroll', throttledScroll);
  • Используется throttle (например, из lodash) для уменьшения числа вызовов update.
  • Особенно важно для мобильных устройств, где частые пересчёты могут вызывать дергание или задержки анимации.

Расширение функциональности

Headroom.js позволяет добавлять кастомные коллбэки при смене состояния:

const headroom = new Headroom(header, {
  onPin: () => console.log('Pinned'),
  onUnpin: () => console.log('Unpinned')
});
  • Можно интегрировать с эффектами появления/исчезания кнопок, изменения прозрачности или других элементов модального окна.
  • Особенно полезно для UX: заголовки и кнопки управления становятся видимыми только при необходимости.

Итоговая структура модального скроллера с Headroom

  1. Выбор контейнера: указывается scroller вместо window.
  2. CSS-анимации: используется transform и transition.
  3. Динамический контент: ResizeObserver + переинициализация Headroom.
  4. События модального окна: init/destroy при открытии/закрытии.
  5. Вложенные скроллы: отдельные экземпляры Headroom для внутренних контейнеров.
  6. Производительность: throttle scroll-событий.
  7. Дополнительные эффекты: коллбэки onPin/onUnpin для управления другими элементами интерфейса.

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