Preventing the annoying over-scrolling inside modals
Oftentimes websites have modals with scrollable content inside. A common annoyance with modals is the ‘over-scrolling’ that happens when the user scrolls past the bottom of the modal’s contents. In other words, the browser starts to scroll the contents of what’s behind the modal.
I was working on modals with my coworker Dom when we discovered an easy way to prevent this with CSS and a bit of Javascript!
The HTML
What we need is a wrapper div that has the same height as its parent element, which in this example is the . The markup looks like this:
The location of the modal div doesn’t really matter since it’s going to be using position: fixed
, but for the purpose of this demo we’ll place it inside the wrapper.
The CSS
Now for the CSS.
.body--has-modal {
overflow-y: scroll;
}
.wrapper {
height: 100%;
overflow-y: scroll;
}
.wrapper--no-scrollbar {
overflow: hidden;
}
When the modal is open, we apply the .body--has-modal
to the to force it to have a scrollbar even if it doesn’t need it. This is done to prevent the content from shifting when the
.wrapper
div’s scrollbar is hidden. The scrollbar is about 15px wide (depending on the browser), thus if you take it away the page’s content will shift.
The styles for .wrapper
ensures that it’s the same height as its parent, and the overflow: scroll
adds a scrollbar. .wrapper--no-scrollbar
simply hides the scrollbar when the modal is active.
The modal’s CSS isn’t that important here, but we’re just going to have a div with a fixed position and some scrollable content inside. The .modal--is-active
class is applied when we want the modal to be shown.
.modal {
display: none;
background: white;
width: 400px;
height: 400px;
position: fixed;
left: calc(50% - 200px);
top: calc(50% - 200px);
overflow-y: scroll;
}
.modal--is-active {
display: block;
}
The Javascript
Here we’re using some jQuery to toggle some classes on and off. Of course you don’t have to use jQuery here, but I’m using it to save some time ๐
$("button").on('click', function(){
$(".wrapper").toggleClass("wrapper--is-active");
$("body").toggleClass("body--has-modal");
$(".modal").toggleClass("modal--is-active");
});
Demo
You can see a quick working demo here. I’ve sprinkled in some extra content and CSS for demonstration purposes.
You can see that in addition to getting rid of the over-scrolling, the content’s scroll position is maintained. On top of that, thanks to the
.wrapper
div’s scrollbar acting as the buffer for the ‘s scrollbar, the width of the page never changes and we don’t see any re-flowing of content.
Hope that helps! Thanks again to Dom for working on this with me!
9 Comments
September 22nd, 2017
This works well to stop page content from shifting, but if you have a background image instead of CSS colors it causes the image to shift over still. Any idea how to prevent that from happening?
February 10th, 2017
Glad it helped Anton!
February 10th, 2017
…. due to DOCTYPE ….
February 10th, 2017
Very helpful post. Thank You!
I realized that the second scrollbar displayed due to . But I don’t know how to solve it.
October 6th, 2016
Strange! I’ll take a look shortly, Bogdan.
October 6th, 2016
I can’t figure-out how to repeat this demo page?
Here is codepen https://codepen.io/Tvoyrotoptal/pen/ozporA where every element is the same ,but its working in a different way.
What im doing wrong?
March 26th, 2016
thanks a lot for this.
October 13th, 2015
Nice post! Consider this mentally logged away for when I inevitably run into this issue in the future ๐
October 13th, 2015
It’s incredible how few websites implement this, yet how much friendlier it is to the user to include it. This also extends to drawers or other popouts that have their own scrollable area.