Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent body scrolling but allow overlay scrolling

I've been searching for a "lightbox" type solution that allows this but haven't found one yet (please, suggest if you know of any).

The behavior I'm trying to recreate is just like what you'd see at Pinterest when clicking on an image. The overlay is scrollable (as in the whole overlay moves up like a page on top of a page) but the body behind the overlay is fixed.

I attempted to create this with just CSS (i.e. a div overlay on top of the whole page and body with overflow: hidden), but it doesn't prevent div from being scrollable.

How to keep the body/page from scrolling but keep scrolling inside the fullscreen container?

like image 445
PotatoFro Avatar asked Feb 14 '12 16:02

PotatoFro


People also ask

How do I stop my body from scrolling?

Approach: A simple solution to this problem is to set the value of the “overflow” property of the body element to “hidden” whenever the modal is opened, which disables the scroll on the selected element.

How do you stop background scrolling in CSS?

Disabling scroll with only CSS. There's another way to disable scrolling that is commonly used when opening modals or scrollable floating elements. And it is simply by adding the CSS property overflow: hidden; on the element you want to prevent the scroll.

How do I stop scrolling when scrolling a div element CSS?

mouseleave(function() { $('body'). bind('mousewheel DOMMouseScroll', function() { return true; }); }); This is stopping all scrolling where as I want scrolling to still be possible inside the container.

How do you make a scrollable overlay in Figma?

On the 'menu' Frame, tick the 'clip content' checkbox on the Design tab, and set the 'scroll overflow' behaviour on the Prototype tab. In this example I've set it to 'Vertical scrolling'. Set your screen as the 'Starting frame', click the play button in the top right, and give yourself a pat on the back.


2 Answers

Theory

Looking at current implementation of the pinterest site (it might change in the future), when you open the overlay a noscroll class is applied to the body element and overflow: hidden is set, thus body is no longer scrollable.

The overlay (created on-the-fly or already inside the page and made visible via display: block, it makes no difference) has position : fixed and overflow-y: scroll, with top, left, right and bottom properties set to 0: this style makes the overlay fill the whole viewport.

The div inside the overlay is instead just in position: static then the vertical scrollbar you see is related to that element. As a result the content is scrollable but overlay remains fixed.

When you close the zoom you hide the overlay (via display: none) and then you could also entirely remove it via javascript (or just the content inside, it's up to you how to inject it).

As a final step you have to also remove the noscroll class to the body (so the overflow property returns to its initial value)


Code

Codepen Example

(it works by changing the aria-hidden attribute of the overlay in order to show and hide it and to increase its accessibility).

Markup
(open button)

<button type="button" class="open-overlay">OPEN LAYER</button> 

(overlay and close button)

<section class="overlay" aria-hidden="true">   <div>     <h2>Hello, I'm the overlayer</h2>     ...        <button type="button" class="close-overlay">CLOSE LAYER</button>   </div> </section> 

CSS

.noscroll {    overflow: hidden; }  .overlay {     position: fixed;     overflow-y: scroll;    top: 0; right: 0; bottom: 0; left: 0; }  [aria-hidden="true"]  { display: none; } [aria-hidden="false"] { display: block; } 

Javascript (vanilla-JS)

var body = document.body,     overlay = document.querySelector('.overlay'),     overlayBtts = document.querySelectorAll('button[class$="overlay"]');  [].forEach.call(overlayBtts, function(btt) {    btt.addEventListener('click', function() {        /* Detect the button class name */      var overlayOpen = this.className === 'open-overlay';       /* Toggle the aria-hidden state on the overlay and the          no-scroll class on the body */      overlay.setAttribute('aria-hidden', !overlayOpen);      body.classList.toggle('noscroll', overlayOpen);       /* On some mobile browser when the overlay was previously         opened and scrolled, if you open it again it doesn't          reset its scrollTop property */      overlay.scrollTop = 0;    }, false);  }); 

Finally, here's another example in which the overlay opens with a fade-in effect by a CSS transition applied to the opacity property. Also a padding-right is applied to avoid a reflow on the underlying text when the scrollbar disappears.

Codepen Example (fade)

CSS

.noscroll { overflow: hidden; }  @media (min-device-width: 1025px) {     /* not strictly necessary, just an experiment for         this specific example and couldn't be necessary         at all on some browser */     .noscroll {          padding-right: 15px;     } }  .overlay {       position: fixed;       overflow-y: scroll;      top: 0; left: 0; right: 0; bottom: 0; }  [aria-hidden="true"] {         transition: opacity 1s, z-index 0s 1s;     width: 100vw;     z-index: -1;      opacity: 0;   }  [aria-hidden="false"] {       transition: opacity 1s;     width: 100%;     z-index: 1;       opacity: 1;  } 
like image 134
Fabrizio Calderan Avatar answered Sep 18 '22 18:09

Fabrizio Calderan


If you want to prevent overscrolling on ios, you can add position fixed to your .noscroll class

body.noscroll{     position:fixed;     overflow:hidden; } 
like image 38
am80l Avatar answered Sep 19 '22 18:09

am80l