Prevent body scrolling when showing modal dialog or popup with overlay. Any content in the background should not be moving. That’s because the user actions and experience should focus on the popup/dialog and any movement (scrolling) in the background could cause confusion. And it becomes more annoying when you try to scroll the content of the popup/dialog but ended up it’s the background content that scrolls.
I was looking for a solution that would work across all major browsers and have been searching for quite a while. There are several solutions:
- overscroll-behavior
- overflow: hidden on body
- position: fixed or position: absolute on body
- listen to touch events to intercept actions using javascript
- html5 dialog element
However, they can’t resolve all my needs or they are overly complicated for a seemingly simple feature.
What is needed?
- Cross browsers. It should work for major browsers including chrome, firefox, safari and edge.
- The content in the background is not scroll-able.
- The content in the dialog/popup is scroll-able.
- Maintain the scroll position of the content in the background.
Make it work to prevent body scrolling
Having tested with most suggested methods found and experiment around a bit, luckily I am able to land on a method using css and simple javascript.
The idea is to use position:fixed. Use it on the child element of body, not body itself. So the html structure would look like this:
1 2 3 4 5 6 7 |
<html> <body> <section class"containter"> Content here </section> </body> </html> |
And apply the following css style to the section element when showing the dialog or popup,
1 2 3 4 |
.no-scroll { position: fixed; width: 100%; } |
The benefits for applying this to child element is that it works for safari and would not mess up the scroll position.
Next is to apply simple javascript to maintain the scroll position:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
function lockBodyScroll(lock) { var tag = document.querySelector('.container'); // the child element of body that contains the long content if (!tag) return; var elem = document.scrollingElement || document.body; if (lock) { var scrollTop = elem.scrollTop; tag.classList.add('no-scroll'); tag.style.top = '-' + scrollTop + 'px'; } else { var top = tag.offsetTop; tag.classList.remove('no-scroll'); tag.style.top = '0px'; elem.scrollTop = - top; } } |
That is. You can now prevent body scrolling for all major browsers. I tested it on iPhone, Samsung Note, Samsung Galaxy and Chrome & Edge on desktop and all worked as expected.
Does it work for you? Or you have a better solution? Please leave a comment and let me know.
Reference read: