Back

Как предотвратить прокрутку страницы при открытом диалоговом окне

Как предотвратить прокрутку страницы при открытом диалоговом окне

Открытие модального диалогового окна должно фокусировать внимание пользователя на этом окне, а не позволять ему прокручивать страницу позади него. Тем не менее, предотвращение прокрутки фона остается удивительно сложной задачей, особенно когда требуется надежное поведение блокировки прокрутки в iOS Safari.

Элемент <dialog> с методом showModal() автоматически блокирует взаимодействие. Браузер помечает все за пределами диалога как inert, предотвращая клики и навигацию с клавиатуры. Но предотвращение прокрутки? Это отдельная проблема, которую платформа не решает полностью.

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

Ключевые выводы

  • Метод showModal() блокирует взаимодействие, но не предотвращает прокрутку фона
  • CSS overflow: hidden работает на десктопе, но не срабатывает в iOS Safari
  • Техника фиксированного body с восстановлением позиции прокрутки обеспечивает наиболее надежное кроссбраузерное решение
  • Используйте overscroll-behavior: contain вместе с другими методами для предотвращения цепочки прокрутки
  • Всегда сохраняйте возможность прокрутки диалога с помощью max-height и overflow-y: auto для доступности

Что делает и не делает showModal()

Когда вы вызываете dialog.showModal(), браузер:

  • Отображает диалог на верхнем слое
  • Создает псевдоэлемент ::backdrop
  • Делает фоновый контент инертным (блокирует взаимодействие с указателем и клавиатурой)
  • Захватывает фокус внутри диалога

Что он не делает: надежно предотвращает прокрутку фона во всех браузерах и устройствах. На некоторых платформах — в первую очередь на мобильных — пользователи все еще могут прокручивать страницу с помощью сенсорных жестов или колеса мыши.

Простой CSS-подход: overflow: hidden

Наиболее распространенное решение использует селектор :has() для обнаружения открытого диалога:

body:has(dialog[open]) {
  overflow: hidden;
}

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

Подвох: Этот подход ненадежен в iOS Safari. Сенсорная прокрутка часто обходит overflow: hidden на body, позволяя пользователям все равно прокручивать фон. Если ваша аудитория включает пользователей мобильного Safari, вам понадобится что-то более надежное.

Техника фиксированного Body для кроссбраузерной надежности

Наиболее надежное кроссбраузерное решение — включая блокировку прокрутки в iOS Safari — использует JavaScript для фиксации body на месте:

let scrollPosition = 0;

function lockScroll() {
  scrollPosition = window.scrollY;
  document.body.style.position = 'fixed';
  document.body.style.top = `-${scrollPosition}px`;
  document.body.style.width = '100%';
}

function unlockScroll() {
  document.body.style.position = '';
  document.body.style.top = '';
  document.body.style.width = '';
  window.scrollTo(0, scrollPosition);
}

Вызывайте lockScroll() при открытии диалога и unlockScroll() при закрытии.

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

Компромисс: Вы можете увидеть сдвиг макета, если полоса прокрутки исчезает. Компенсируйте это, добавив padding-right, равный ширине полосы прокрутки при блокировке.

CSS overscroll-behavior для цепочки прокрутки

Свойство overscroll-behavior предотвращает цепочку прокрутки — когда прокрутка внутри одного элемента вызывает прокрутку родителя после достижения границы.

dialog {
  overscroll-behavior: contain;
}

dialog::backdrop {
  overscroll-behavior: contain;
}

Последние версии Chromium (конец 2025+) улучшили это поведение, заставив его работать даже на непрокручиваемых контейнерах. Это помогает с блокировкой прокрутки HTML-диалога в Chrome, но другие браузеры еще не полностью догнали.

Важно: CSS overscroll-behavior в модальных решениях предотвращает цепочку прокрутки, а не саму прокрутку. Если пользователь прокручивает непосредственно backdrop или body, overscroll-behavior не остановит это. Используйте это вместе с другими техниками, а не в качестве замены.

Особенности iOS Safari, которые следует знать

iOS Safari представляет уникальные проблемы для предотвращения прокрутки фона в модальных реализациях:

  • overflow: hidden на body не блокирует надежно сенсорную прокрутку
  • Эффект резиновой ленты при избыточной прокрутке может вызвать движение фона
  • Появление виртуальной клавиатуры может вызвать неожиданное поведение прокрутки

Техника фиксированного body остается наиболее надежным решением. Некоторые разработчики также добавляют touch-action: none к элементу backdrop (не к самому диалогу), хотя это может помешать прокрутке внутри диалога, если применяется слишком широко.

Сохраняйте возможность прокрутки диалога

Один критический момент: если содержимое вашего диалога превышает высоту viewport, сам диалог должен прокручиваться. Установите соответствующие max-height и overflow-y: auto на диалоге:

dialog {
  max-height: 90vh;
  overflow-y: auto;
}

Блокировка всей прокрутки создает проблемы с доступностью. Пользователи должны иметь доступ ко всему содержимому диалога.

Заметка о Popover API

Popover API существует для легковесных оверлеев, но не является прямой заменой модальных диалогов. Popover не блокируют фоновое взаимодействие таким же образом, и поведение блокировки прокрутки отличается. Для настоящих модальных окон, требующих блокировки прокрутки, придерживайтесь <dialog> и showModal().

Выбор подхода

Для приложений только для десктопа часто достаточно CSS-подхода :has() с overflow: hidden. Для кроссбраузерной надежности — особенно в iOS Safari — используйте технику фиксированного body с восстановлением позиции прокрутки. Наложите overscroll-behavior: contain сверху для предотвращения цепочки прокрутки в поддерживающих браузерах.

Заключение

Ни одно решение не работает идеально везде. Техника фиксированного body обеспечивает наиболее надежную кроссбраузерную блокировку прокрутки, особенно для iOS Safari. Комбинируйте ее с overscroll-behavior: contain для дополнительной защиты от цепочки прокрутки в браузерах Chromium. Тестируйте на реальных устройствах, особенно в iOS Safari, и примите, что некоторые граничные случаи могут требовать компромисса.

Часто задаваемые вопросы

iOS Safari обрабатывает сенсорную прокрутку иначе, чем десктопные браузеры. Система сенсорных событий браузера может обходить overflow hidden на элементе body, позволяя прокрутку фона даже когда свойство установлено. Техника фиксированного body работает, потому что она физически позиционирует body за пределами экрана, делая прокрутку невозможной, а не просто скрытой.

Да, может. Когда полоса прокрутки исчезает, контент может сдвинуться, чтобы заполнить это пространство. Чтобы предотвратить это, вычислите ширину полосы прокрутки и добавьте эквивалентный padding-right к body при блокировке прокрутки. Удалите padding при разблокировке. Это поддерживает согласованный макет на протяжении всего взаимодействия с модальным окном.

Popover API служит другой цели. Он создает легковесные оверлеи без блокировки фонового взаимодействия или предоставления тех же возможностей блокировки прокрутки. Для настоящих модальных окон, где пользователи должны взаимодействовать с диалогом перед продолжением, используйте элемент dialog с showModal для правильного захвата фокуса и инертного поведения.

Установите overscroll-behavior contain на диалоге, чтобы предотвратить цепочку прокрутки к фону. Убедитесь, что ваш диалог имеет max-height и overflow-y auto, чтобы внутреннее содержимое прокручивалось правильно. Избегайте применения touch-action none ко всему диалогу, так как это блокирует легитимную прокрутку внутри области содержимого модального окна.

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay