Как стать автором
Обновить

Запрет прокрутки при открытии модального окна

Время на прочтение3 мин
Количество просмотров44K
Автор оригинала: Brad Wu

Приветствую. Представляю вашему вниманию перевод статьи «Prevent Page Scrolling When a Modal is Open», опубликованной 3 июня 2019 года за авторством Brad Wu



Бывало у вас такое? Открываете модальное окно, прокручиваете, а после закрытия оказываетесь на странице в позиции, отличающейся от той, с которой его открывали.


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



Порой это не становится проблемой. Например, когда высота содержимого страницы не превышает высоту области видимости экрана пользователя. Однако, в остальных случаях мы неизбежно сталкиваемся с этим. Хорошей новостью является то, что мы можем предотвратить появление скролла с помощью трюка с использованием CSS и JavaScript.


Давайте начнём с чего-нибудь простого


При открытии модального окна можно просто устанавливать элемент body по высоте равным области видимости (viewport) и скрывать вертикальную прокрутку:


body.modal-open {
  height: 100vh;
  overflow-y: hidden;
}

Решение имеет право на существование, но если перед открытием модального окна мы прокрутим элемент body, получим небольшую перерисовку содержимого. Ширина области видимости расширяется приблизительно на 15px, что соответствует ширине полосы прокрутки.



Давайте немного отрегулируем правый padding для body, чтобы избежать этого.


body {
  height: 100vh;
  overflow-y: hidden;
  padding-right: 15px; /* Avoid width reflow */
}

Обратите внимание – чтобы это сработало, модальное окно должно быть по высоте меньше области видимости. Иначе полоса прокрутки будет нужна.


Отлично. А что насчёт мобильных?


Это решение отлично работает как на десктопах, так и на мобильных Android-устройствах. Однако, Safari для iOS требует немного больше внимания, поскольку body всё ещё может прокручиваться, когда модальное окно открыто.


Как альтернатива, мы можем задать для body фиксированное позиционирование.


body {
  position: fixed;
}

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



Вот поэтому нам не обойтись без JavaScript


Можем использовать JavaScript, чтобы избежать всплытия события прикосновения к экрану. Все мы знаем, что при открытии модального окна, у него должен быть фоновый слой. К сожалению, в iOS метод stopPropagation() с событиями прикосновения работает немного неуклюже. Но у preventDefault() таких проблем нет. Это значит, что мы должны добавить обработчики событий к каждому DOM-элементу внутри модального окна, а не только к слою подложки или самому модальному окну. Хорошая новость в том, что много JavaScript-библиотек могут делать это, включая старый добрый jQuery.


Ах да, и ещё кое-что. Что если нам нужна прокрутка внутри модального окна? Нам всё ещё нужно вызвать реакцию на событие свайпа, но при достижении верха или низа модального окна, нам всё ещё нужно предотвращать всплытие. Кажется очень сложным (?? так что мы ещё не полностью выкарабкались).


Давайте улучшим фиксированный body


Вот с чем мы работали:


body {
  position: fixed;
}

С помощью JavaScript можно вычислить положение прокрутки страницы, и добавлять это значение в CSS. Благодаря этому body не будет прокручиваться обратно в начальную позицию.


// When the modal is shown, we want a fixed body
document.body.style.position = 'fixed';
document.body.style.top = `-${window.scrollY}px`;

// When the modal is hidden, we want to remain at the top of the scroll position
document.body.style.position = '';
document.body.style.top = '';

Данное решение работает, но после закрытия окна всё ещё происходит небольшой сдвиг. А именно, похоже, когда модальное окно открыто и для body задано фиксированное позиционирование, страница уже теряет положение прокрутки. Следовательно, нам нужно восстановить положение. Давайте изменим JavaScript, чтобы учитывать этот момент.


// Когда модальное окно скрыто...
const scrollY = document.body.style.top;
document.body.style.position = '';
document.body.style.top = '';
window.scrollTo(0, parseInt(scrollY || '0') * -1);

Вот и всё. Теперь body не будет прокручиваться при открытом модальном окне, а положение прокрутки сохраняется и при открытии и при закрытии модального окна.


Теги:
Хабы:
Рейтинг0
Комментарии0

Публикации

Истории

Работа

Ближайшие события

Конференция «Я.Железо»
Дата18 мая
Время14:00 – 23:59
Место
МоскваОнлайн
Антиконференция X5 Future Night
Дата30 мая
Время11:00 – 23:00
Место
Онлайн
Конференция «IT IS CONF 2024»
Дата20 июня
Время09:00 – 19:00
Место
Екатеринбург