CraZyDroiD
CraZyDroiD

Reputation: 7105

Prevent background scrolling when overlay appears

I have written my own modal classes using css and have used it in my application successfully. However the issue i'm facing is when the overlay is open i can still scroll the background contents. How can i stop scrolling background contents when my modal/overlay is open?

This is my modal which opens on top of the overlay

<div>
  <div className="overlay"></div>
  {this.props.openModal ?
  <div>
    <div className="polaroid sixten allcmnt_bg_clr horiz_center2">
      {}
      <div className="mobile_header">
        <PostHeader/>
      </div>
      <div className="mobile_renderPost">
        { this.renderPostType() }
      </div>
      <div className="mobile_post_bottom"></div>
    </div>
  </div> : null}
</div>

my overlay css

.overlay {
  background-color: rgba(0, 0, 0, .70);
  position: fixed;
  width: 100%;
  height: 100%;
  opacity: 1;
  left: 0;
  right: 0;
  -webkit-transition: opacity .25s ease;
  z-index: 1001;
  margin: 0 auto;
}

Upvotes: 24

Views: 59808

Answers (8)

Dragos Catalin
Dragos Catalin

Reputation: 103

This is by far the easiest way to do it!

If you want to use classic css:

html:has(section[role="dialog"][data-open="true"]) {
  overflow: hidden;
}

If you want to use tailwind:

<html lang="en" className={`has-[section[role='dialog'][data-open='true']]:overflow-hidden`}>

Replace the selectors with the ones you need

Upvotes: 1

Th3S4mur41
Th3S4mur41

Reputation: 2285

Combining the overflow: hidden solution with modern technique works perfectly

html:has(dialog[open]) {
  /* remove the main scrollbar when dialog is open */
  overflow: hidden;
}

Upvotes: 13

Flow
Flow

Reputation: 39

All the answers so far (Oct. 2022) suggest to add overflow: hidden dynamically to either 'body' or 'html' when you open the modal/pop-up. This works if 'html' or 'body' are actually your scrolling elements and fixes the somewhat counterintuitive over-scroll behavior of position: fixed elements.

I've tried to use overscroll-behavior instead to fix the issue and this can work, but requires scrollable elements (with actual overflow) inside your modal and is not very reliable if the user simply decides to touch your overlay outside of the "locked" elements.

Depending on your page design there is another option that requires you to split content and overlay and set the 'html' and 'body' height explicitly to 100%. Here is a complete example:

<html>
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <style>
        * {
            box-sizing: border-box;
        }
        body {
            position: relative;
            margin: 0;
            padding: 0;
            font-family: sans-serif;
            font-size: 16px;
        }
        .content {
            padding: 32px;
            border: 1px solid #000;
        }
        .overlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(255, 255, 255, .5);
            backdrop-filter: blur(5px);
            display: flex;
            justify-content: center;
            align-items: center;
            z-index: 1001;
        }
        .info-box {
            background: #000;
            color: #eee;
            border-radius: 5px;
            width: 240px;
            height: 240px;
            padding: 16px;
        }
        
        /* scroll fix */
        html, body {
            height: 100%;
        }
        .content {
            max-height: 100%;
            overflow-y: auto;
        }
    </style>
</head>
<body>
    <div class="content">
        <h2>Overlay Background Scroll Test</h2>
        <p>Use a window size of around 320x480 for optimal testing (e.g. via device-toolbar).</p>
        <h3>Scrollable Page</h3>
        <p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.</p>
        <p>At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</p>
        <p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.</p>
        <button onclick="document.querySelector('.overlay').style.display='flex';">open</button>
    </div>
    <div class="overlay">
        <div class="scroll-fix">
            <div class="info-box">
                <h3>Pop-Up Message</h3>
                <p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.</p>
                <button onclick="document.querySelector('.overlay').style.display='none';">close</button>
            </div>
        </div>
    </div>
</body>

The important section is the last CSS entry. This will make your .content element the main scroll element:

html, body {
    height: 100%;
}
.content {
    max-height: 100%;
    overflow-y: auto;
}

There is a disadvantage to this approach though. Most mobile browsers will not be able to automatically hide their URL-bar through scrolling anymore, because this seems to depend on html or body element scrolling :-(.

Upvotes: 0

Toomuchrice4u
Toomuchrice4u

Reputation: 648

I had this problem too and tried every answer from setting the height on the body element to 100% or 100vh and overflow: hidden. This caused a few issues for me, starting with that using the hidden overflow with the 100vh made the page jump to the top whenever clicking the hamburger menu button.

The solution: adding the overflow:hidden property to the html tag. This worked perfectly where the menu would open, prevent the page from scrolling, and remain where the user is on the page without it jumping.

Since it looks like you're using React, here is an example of how I used it:

.lock-scroll {
  overflow: hidden;
}
const [open, setOpen] = useState(false)
useEffect(() => {
  const html = document.getElementsByTagName('html')[0]

  if (open) {
    html.classList.add('lock-scroll')
  } else {
    html.classList.remove('lock-scroll')
  }
  return (): void => {
    html.classList.remove('lock-scroll')
  }
}, [open])

Upvotes: 8

Austin Jones
Austin Jones

Reputation: 709

Using JavaScript to add a class to the body with

overflow:hidden;

will work in most cases, but I beleive Safari on iPhone will still scroll slightly with jitter due to Touch Move and something like this will be needed.

function handleTouchMove(e)
{
  e.preventDefault();
}
function lockscreen()
{
  var body = document.getElementById("body");
  body.className += " lock-screen";
  body.addEventListener('touchmove', handleTouchMove, false);
}
function unlock()
{
  var body = document.getElementById("body");
 body.classList.remove("lock-screen");
 body.removeEventListener('touchmove', handleTouchMove);
}

to stop the user from still scrolling

Upvotes: 3

Get Off My Lawn
Get Off My Lawn

Reputation: 36351

When the modal opens, hide the x/y scroll bars on the body.

.no-scroll {
    overflow: hidden;
}

Using JavaScript add the class to the body:

<body class="no-scroll">

</body>

Once you close the modal remove the class.

Upvotes: 1

Lukas Safari
Lukas Safari

Reputation: 1978

One approach is hidden the overflow of the body element.

like this:

body.modal-open{
    overflow:hidden;
}

so in this case when you popup the modal you add a class to body and then when you close it you remove that class.

another approach is using a javascript to disable the scroll like this:

   document.documentElement.style.overflow = 'hidden';
   document.body.scroll = "no";

and then return it with

 document.documentElement.style.overflow = 'scroll';
 document.body.scroll = "yes";

Upvotes: 33

adeltahir
adeltahir

Reputation: 1097

When you open the modal, you can add overflow: hidden; to the body's style.

Or,

body.modal-opened {
   overflow: hidden;
}

And add modal-opened class to the body when opening and remove when you close the dialog.

Upvotes: 5

Related Questions