tetchi blog

Tetchi's blog about life and stuff

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.

bad-scrolling

Scrolling past the bottom of a modal will cause the content behind it to scroll, and vice versa. Demo of this annoying behaviour here.

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

  • Carson
    Carson on 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.

  • Grant Lucas
    Grant Lucas on October 13th, 2015

    Nice post! Consider this mentally logged away for when I inevitably run into this issue in the future :)

  • Ahmed
    Ahmed on March 26th, 2016

    thanks a lot for this.

  • Bogdan
    Bogdan on 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?

  • tetchi
    tetchi on October 6th, 2016

    Strange! I’ll take a look shortly, Bogdan.

  • Anton
    Anton on 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.

  • Anton
    Anton on February 10th, 2017

    …. due to DOCTYPE ….

  • tetchi
    tetchi on February 10th, 2017

    Glad it helped Anton!

  • Joel
    Joel on 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?

Post a comment