Блокировка прокрутки страницы c открытым модальным окном
Пожалуйста, остановите меня, если слышали об этом раньше. Вы открываете модалку, прокручиваете ее, зкрываете, и оказываетесь где-то в другой части страницы относительно места, где вы открывали модалку.
Это происходит потому что модальные окна такие же элементы, как и все остальные на странице. Они могут оставаться на своем месте (если такова их функция), но вся остальная страница продолжает себя вести, как и всегда.
Иногда это не проблема, например, когда высота экрана остается фиксированной. Во всех остальных случаях нам приходится смотреть на полотно прокрутки. Хорошие новости заключаются в том, что мы можем предотвратить это при помощи щепотки CSS (и JS) трюков.
Давайте начнем с чего-нибудь простого
Мы можем решить большую часть проблем с прокруткой страницы, устанавив высоту body на полную высоту экрана и скрыв вертикальную прокрутку, пока модалка открыта.
Это, конечно хорошо, и все такое, но если мы проскроллим body до того, как откроем модалку, мы получим небольшое смещение по горизонтали. Ширина экрана расширена примерно на 15 пикселей, что на самом деле является шириной скроллбара.
Давайте немного подправим у body правый padding, чтобы исправить это.
Помните, что для того, чтобы это работало, модальное окно должно быть меньше, чем высота экрана. В противном случае будет нужна прокрутка.
Отлично, а что там насчет мобилок?
Это решение работает довольно хорошо на десктопах так же, как и на Android’ах. Тем не менее, Safari на iOS нуждается в «большей любви», поскольку body продолжает скроллиться, пока модалка открыта.
Чтобы обойти это, мы можем установить «position: fixed» у body.
Заработало! body не будет реагировать на касания экрана. Однако здесь есть небольшая проблема. Допустим, тригер модалки находится на странице ниже, и мы кликаем, чтобы открыть ее. Отлично! Но теперь мы автоматически перемещаемся обратно к верху страницы, что так же дизориентирует, как и поведение прокрутки, которое мы пытаемся разрешить.
Вот почему мы должны обратиться к JS
Мы можем использовать JS, чтобы избежать всплытия события касания. Все мы знаем, что пока модалка открыта, у нее должна быть фоновая подложка. К сожалению, использовать stopPropagation в iOS для событий касания немного неудобно. Но preventDefault работает хорошо. Это означает, что мы должны добавить слушателей событий в каждый узел DOM, содержащийся в модалке — не только на подложку или саму модалку. Хорошая новость в том, что множество JS-библиотек могут это делать, включая старый добрый jQuery.
И вот еще что: что если нам нужна прокрутка внутри модалки? Мы должны отвечать на тригер события касания, но когда достигаем верхней или нижней границы модалки, мы должны предотвращать всплытие. Звучит очень сложно, так что мы еще не закончили.
Давайте усовершенствуем подход с фиксированным body
Вот, с чем мы работали:
Если мы знаем высоту прокрутки, и добавим ее в CSS, тогда body не будет прокручиваться обратно к верху страницы, и проблема решена. Мы можем использовать для этого JS, высчитывая высоту прокрутки, и «запоминая» это значение в стилях для body:
Это работает, но здесь есть небольшая ошибка. В частности, страница уже «теряет» свое положение прокрутки, когда модалка открыта и body устанавливается фиксированная позиция. Так что нам нужно восстановить положение. Давайте изменим код, чтобы учесть это.
Вот и все! body больше не прокручивается, когда модалка открыта, а положение прокрутки сохраняется при открытом и закрытом состоянии модалки. Шазам!
In React, how to prevent body from scrolling when a modal is opened
In HTML, modal usually refers to an element with the following CSS:
However, scrolling inside a modal will lead to body scrolling which is kind of annoying. I searched on the internet but didn’t find a perfect solution(especially for React), so I did some experiments and found a solution which works well in Chrome, Firefox and Safari.
How I define and use the Modal component
I just wanna do a quick explanation of how I define my Modal component for a better understanding, you can skip this section if you only interesting in the final solution.
React Frontend structure:
How to invoke Modal in a page:
Final Solution
- when opening the modal, set the document.body as position: fixed and set its offset based on the current scrolling. If only applied position: fixed, the body will scroll to the top, so we need to make the body align with its previous offset manually.
- when closing the modal, clean the style on the document.body and scroll the body to the previous position. If only clean the style, the body will start from the top, that’s why we have to make use of window.scrollTo to scroll the body to the previous offset.
About Yet
Yet is an online tool which helps people to build one-page business website without any code and design required.
Prevent Page Scrolling When a Modal is Open
Please stop me if you’ve heard this one before. You open a modal, scroll through it, close it, and wind up somewhere else on the page than you were when you opened the modal.
That’s because modals are elements on a page just like any other. It may stay in place (assuming that’s what it’s meant to do) but the rest of page continues to behave as normal.
Sometimes this is a non-issue, like screens that are the exact height of the viewport. Anything else, though, we’re looking at Scroll City. The good news is that we can prevent that with a sprinkle of CSS (and JavaScript) trickery.
Let’s start with something simple
We can make a huge dent to open-modal-page-scrolling ™ by setting the height of the entire body to the full height of the viewport and hiding vertical overflow when the modal is open:
That’s good and all, but if we’ve scrolled through the <body> element before opening the modal, we get a little horizontal reflow. The width of the viewport is expanded about 15 pixels more, which is exactly the with of the scroll bar.
Let’s adjust the right padding of the body a bit to avoid that.
Note that the modal needs to be shorter than the height of the viewport to make this work. Otherwise, the scroll bar on the body will be necessary.
Great, now what about mobile?
This solution works pretty great on desktop as well as Android Mobile. That said, Safari for iOS needs a little more love because the body still scrolls when a modal is open when tapping and moving about the touchscreen.
We can set the body to a fixed position as a workaround:
Works now! The body will not respond when the screen is touched. However, there’s still a “small” problem here. Let’s say the modal trigger is lower down the page and we click to open it up. Great! But now we’re automatically scrolled back up to the top of the screen, which is just as disorientating as the scrolling behavior we’re trying to resolve.
That’s why we’ve gotta turn to JavaScript
We can use JavaScript to avoid the touch event bubble. We all know there should be a backdrop layer when a modal is open. Unfortunately, stopPropagation is a little awkward with touch in iOS. But preventDefault works well. That means we have to add event listeners in every DOM node contained in the modal — not just on the backdrop or the modal box layer. The good news is, many JavaScript libraries can do this, including good ol’ jQuery.
Oh, and one more thing: What if we need scrolling inside the modal? We still have to trigger a response for a touch event, but when reaching the top or bottom of the modal, we still need to prevent bubbling. Seems very complex, so we’re not totally out of the woods here.
Let’s enhance the fixed body approach
This is what we were working with:
If we know the top of the scroll location and add it to our CSS, then the body will not scroll back to the top of the screen, so problem solved. We can use JavaScript for this by calculating the scroll top, and add that value to the body styles:
This works, but there’s still a little leakage here after the modal is closed. Specifically, it appears that the page already loses its scroll position when the modal is open and the body set to be fixed. So we have to retrieve the location. Let’s modify our JavaScript to account for that.
That does it! The body no longer scrolls when a modal is open and the scroll location is maintained both when the modal is open and when it is closed. Huzzah!
Запрет прокрутки при открытии модального окна

Бывало у вас такое? Открываете модальное окно, прокручиваете, а после закрытия оказываетесь на странице в позиции, отличающейся от той, с которой его открывали.
Подобное может происходить из-за того, что модальные окна являются такими же элементами страницы, как и любые другие. Они могут располагаться в фиксированной позиции, не препятствуя работе с остальным содержимым документа.
Порой это не становится проблемой. Например, когда высота содержимого страницы не превышает высоту области видимости экрана пользователя. Однако, в остальных случаях мы неизбежно сталкиваемся с этим. Хорошей новостью является то, что мы можем предотвратить появление скролла с помощью трюка с использованием CSS и JavaScript.
Давайте начнём с чего-нибудь простого
При открытии модального окна можно просто устанавливать элемент body по высоте равным области видимости (viewport) и скрывать вертикальную прокрутку:
Решение имеет право на существование, но если перед открытием модального окна мы прокрутим элемент body , получим небольшую перерисовку содержимого. Ширина области видимости расширяется приблизительно на 15px, что соответствует ширине полосы прокрутки.
Давайте немного отрегулируем правый padding для body , чтобы избежать этого.
Обратите внимание – чтобы это сработало, модальное окно должно быть по высоте меньше области видимости. Иначе полоса прокрутки будет нужна.
Отлично. А что насчёт мобильных?
Это решение отлично работает как на десктопах, так и на мобильных Android-устройствах. Однако, Safari для iOS требует немного больше внимания, поскольку body всё ещё может прокручиваться, когда модальное окно открыто.
Как альтернатива, мы можем задать для body фиксированное позиционирование.
Работает. Теперь body не будет реагировать на свайпы по экрану. Тем не менее, небольшая проблема всё же остаётся. Если кнопка, открывающая модальное окно, находится внизу страницы и чтобы её увидеть, нужно прокручивать страницу, при нажатии на эту кнопку и открытии модального окна, будет происходить автоматическая прокрутка страницы вверх, что может дезориентировать так же, как и фоновая прокрутка body , от которой мы пытаемся избавиться.
Вот поэтому нам не обойтись без JavaScript
Можем использовать JavaScript, чтобы избежать всплытия события прикосновения к экрану. Все мы знаем, что при открытии модального окна, у него должен быть фоновый слой. К сожалению, в iOS метод stopPropagation() с событиями прикосновения работает немного неуклюже. Но у preventDefault() таких проблем нет. Это значит, что мы должны добавить обработчики событий к каждому DOM-элементу внутри модального окна, а не только к слою подложки или самому модальному окну. Хорошая новость в том, что много JavaScript-библиотек могут делать это, включая старый добрый jQuery.
Ах да, и ещё кое-что. Что если нам нужна прокрутка внутри модального окна? Нам всё ещё нужно вызвать реакцию на событие свайпа, но при достижении верха или низа модального окна, нам всё ещё нужно предотвращать всплытие. Кажется очень сложным (?? так что мы ещё не полностью выкарабкались).
Давайте улучшим фиксированный body
Вот с чем мы работали:
С помощью JavaScript можно вычислить положение прокрутки страницы, и добавлять это значение в CSS. Благодаря этому body не будет прокручиваться обратно в начальную позицию.
Данное решение работает, но после закрытия окна всё ещё происходит небольшой сдвиг. А именно, похоже, когда модальное окно открыто и для body задано фиксированное позиционирование, страница уже теряет положение прокрутки. Следовательно, нам нужно восстановить положение. Давайте изменим JavaScript, чтобы учитывать этот момент.
Вот и всё. Теперь body не будет прокручиваться при открытом модальном окне, а положение прокрутки сохраняется и при открытии и при закрытии модального окна.