На главную

Управляем «протягиванием» прокрутки: разбор overscroll‑behavior


Распространённый сценарий: вы прокручиваете модальное окно, доезжаете до конца и… вместо того чтобы остановиться, браузер продолжает тянуть основной документ. Это так называемый «scroll chaining»: событие прокрутки передаётся родительскому контейнеру. На мобильных устройствах этот эффект сопровождается bounce‑анимацией или pull‑to‑refresh. Для некоторых интерфейсов такое поведение отвлекает пользователя и может мешать вашему коду.

В этой статье я расскажу, как CSS‑свойство overscroll-behavior помогает контролировать такие эффекты, избавится от хака overflow: hidden, а также рассмотрим практические примеры и подводные камни.

Цепочка скролов

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

Почему scroll chaining — проблема?

Когда элемент со своей прокруткой достигает границы, браузер по умолчанию передаёт прокрутку родительскому элементу. Такое поведение особенно заметно в следующих ситуациях:

  • Модальные окна. Пользователь читает содержимое модального окна, но при достижении конца внезапно начинает прокручиваться страница под ним.
  • Длинные боковые панели и мобильные меню. Список пунктов меню выходит за пределы экрана; прокрутив до конца, пользователь случайно начинает двигать основной контент.
  • Бесконечные списки. Совмещение bounce‑эффекта и подгрузки данных может запутать пользователя.

Раньше для предотвращения прокрутки родительского элемента использовали JavaScript: при открытии модального окна на body навешивали класс с overflow: hidden и даже position: fixed. Такой подход ломает позиционирование, особенно в Safari, и вызывает «перескок» страницы.

Что делает overscroll‑behavior?

Свойство overscroll-behavior задаёт, что должно происходить при достижении края области прокрутки. Оно описано в спецификации CSS Overscroll Behavior Module Level 1 и является сокращением для overscroll-behavior-x и overscroll-behavior-y. Поддержку в браузерах посмотрите в caniuse.

Основной синтаксис:

/* Ключевые значения */
overscroll-behavior: auto; /* значение по умолчанию */
overscroll-behavior: contain;
overscroll-behavior: none;

/* Два значения: для осей X и Y */
overscroll-behavior: auto contain;

Перечислим значения в виде таблицы:

ЗначениеЧто делает
autoСтандартное поведение: событие прокрутки передаётся родителю, работают bounce‑эффекты.
containОстанавливает scroll chaining: прокрутка ограничена текущим контейнером, но визуальные эффекты (bounce/refresh) сохраняются.
noneЗапрещает scroll chaining и отключает bounce и pull‑to‑refresh.

Если указать два значения, первое относится к горизонтальной оси, второе — к вертикальной. Для отдельного управления можно использовать составные свойства overscroll-behavior-x и overscroll-behavior-y, а также логические варианты overscroll-behavior-inline и overscroll-behavior-block для учёта writing-mode.

Ограничение: только scroll containers

Свойство работает лишь на элементах с собственным скроллом. Ифреймы не являются scroll containers, поэтому overscroll-behavior на них не действует; задайте его на <html> и <body> внутри документа iframe.

Как отключить pull‑to‑refresh и bounce

Чтобы убрать стандартную анимацию перетягивания в мобильных браузерах, достаточно объявить overscroll-behavior-y на корневом элементе:

html {
  overscroll-behavior-y: contain;
}

Это остановит прокрутку при достижении верхней границы, но bounce‑эффект сохранится. Если нужно полностью отключить «резинку», используйте значение none:

html {
  overscroll-behavior-y: none;
}

Такой подход пригодится при реализации собственной анимации pull‑to‑refresh либо бесконечной ленты, чтобы визуальный «отскок» не сбивал пользователей.

Примеры использования

Длинная мобильная навигация

Когда пунктов меню слишком много, простое движение пальцем приводит к прокрутке страницы. Добавьте overscroll-behavior-y: contain и overflow-y: auto на контейнер навигации:

.nav {
  overscroll-behavior-y: contain;
  overflow-y: auto;
}

Теперь прокрутка остановится на конце списка, не затрагивая <body>.

Фиксированная боковая панель

Если боковая панель фиксирована (position: fixed) и имеет собственную прокрутку, то без регулировки она будет прокручивать основную колонку. Настройте:

.aside {
  overscroll-behavior-y: contain;
}

По достижении низа контент останется на месте.

Модальные окна с длинным списком

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

.list-wrapper {
  overscroll-behavior-y: contain;
  overflow-y: auto;
  max-height: 130px;
}

Горизонтальные карусели

Для горизонтальных списков (например, «Истории» в социальных сетях) применяют overscroll-behavior-x: contain, чтобы зафиксировать прокрутку на этом уровне:

.list {
  overscroll-behavior-x: contain;
  overflow-x: auto;
}

Блокировка прокрутки под модальным окном

Самый простой способ предотвратить скролл родительского элемента при появлении модального окна — задать overscroll-behavior: contain или none на <html>/<body>. Это изящная альтернатива старому методу с overflow: hidden и избавляет от необходимости фиктивно фиксировать body.

Отдельные оси и логические свойства

Иногда нужно контролировать только одну ось. Например, для чат‑компонента достаточно запретить вертикальный scroll chaining. Это делается с помощью overscroll-behavior-y. Для горизонтальных списков — overscroll-behavior-x. Логические свойства overscroll-behavior-inline и overscroll-behavior-block облегчают поддержку разных направлений письма. Они переключают управление между осями x и y в зависимости от writing-mode

СвойствоОсьОсобенность
overscroll-behavior-xгоризонтальнаяКонтролирует прокрутку слева направо/справа налево.
overscroll-behavior-yвертикальнаяКонтролирует прокрутку сверху вниз.
overscroll-behavior-inlineinline‑осьПереключается между x и y в зависимости от режима письма.
overscroll-behavior-blockблок‑осьАналогично inline‑варианту, только для блокового направления.

Источники