505

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?

Konstantin Grushetsky
  • 892
  • 1
  • 12
  • 28
PotatoFro
  • 5,548
  • 4
  • 17
  • 22
  • isnt this just an "overlay" plugin like hundrets out there using position fixed with overflow-y scroll ? – ggzone Feb 14 '12 at 16:17
  • 4
    Link to Pinterest doesn't help, as its content is behind a login wall. – 2540625 Jun 10 '15 at 02:54
  • 8
    **2017 Update:** even if you do log in to Pinterest, what you find is that the overlay effect described in the OP no longer exists - instead when you click on an image you just navigate to an ordinary page displaying a large version of the image. – Annabel Nov 23 '17 at 00:48

19 Answers19

682

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; 
}
Fabrizio Calderan loves trees
  • 109,094
  • 24
  • 154
  • 160
  • 8
    This is the correct answer. Someone should give it a check mark as it is correct. – Scoota P Feb 14 '12 at 21:11
  • 67
    This is absolutely correct, but be warned, it doesn't work in mobile safari. The background will continue to scroll and your overlay will be fixed. – a10s Apr 27 '12 at 17:33
  • 4
    Does anyone have the solution to iOS 5 Mobile Safari? – Michael Jun 19 '13 at 01:36
  • 1
    This doesn't really work on mobile — overflow hidden hides scroll bars but still allows dragging the area with touch. Possibly relevant: http://www.kylejlarson.com/blog/2011/fixed-elements-and-scrolling-divs-in-ios-5/ and http://stackoverflow.com/questions/10592411/disable-scrolling-in-all-mobile-devices – RobW Aug 16 '13 at 22:32
  • 21
    It works fine. Scrollable viewport container `div` must have CSS style `position:fixed`, and have vertical overflow scrollable. I successfully used `overflow-y:auto;` and for iOS momentum/inertia scrolling, I added `-webkit-overflow-scrolling:touch;` to the CSS. I used `display:block;`, `width:100%;`, and `height:100%;` CSS to have a full-page viewport. – Slink Jun 23 '14 at 16:32
  • 2
    `-webkit-overflow-scrolling:touch;` solves it on mobile Safari, thanks! – adriantoine Jul 08 '14 at 14:19
  • you should set ```height```, ```width```, ```top```, and ```left``` for instance, not ie. ```right``` and ```left``` together -- that doesn't work in firefox. – Ryan Taylor Jul 30 '14 at 21:09
  • 10
    Sorry, I do not understand. Setting the body to overflow: hidden does not disable the elastic body scrolling on my iPad iOS7. So I had to add within my js: document.body.addEventListener( 'touchmove', function(event) {event.preventDefault();}, false ); which SADLY disables all elements from being scrollable, too. Have not found any solution so far (without extra plugIns) – Garavani Sep 04 '14 at 07:12
  • 23
    If the user has already scrolled the body down to bottom when the overlay is activated, this will cause the body to jump up to top when you set overflow: hidden; Any way around this? – Björn Andersson Apr 07 '15 at 14:17
  • @BjörnAndersson I noticed this same thing also, and this is not helpful without the body staying where it was. I am using an overlay menu on mobile devices that opens and closes and takes up full screen when opened (however, it is a sticky menu and can be oopened from anywhere on the pages), however, when user closes it they are back up at the top of the page instead of where they left off at. – Solomon Closson May 23 '15 at 22:01
  • @BjörnAndersson in which browser/version/platform do you see the page jump at the overlay opening? – Fabrizio Calderan loves trees Feb 17 '16 at 09:22
  • you can cover the body with a scrollable div to intercept the body scroll http://stackoverflow.com/a/39139591/1563181 – YarGnawh Aug 25 '16 at 08:01
  • @BjörnAndersson saving and restoring the scroll position can be done via var x = window.scrollY; when opening the overlay and then when closing the overlay window.scrollTo(0, x); – jlguenego Feb 11 '17 at 16:12
  • Works, but pretty wonky on iOS. If you stop scrolling in the overlay for some seconds and then again the body will scroll. If you scroll top in the overlay right when its opened the body will scroll. – GDY May 30 '17 at 08:06
  • 3
    iOS is incorrect in how it handles the viewport size, it should be completely disregarded and left to suffer with its drawbacks. We can't be fixing horrible implementations of very basic concepts. – mystrdat Jun 26 '17 at 12:28
  • This is not a very good solution at all. If you have a link half way down the page that opens a dialog, for instance, this solution will force the page to scroll to top, then show the dialog. When you close the dialog and remove the noscroll class, you are still at the top of the page and not positioned where the dialog was initiated by. – Ed DeGagne Sep 21 '17 at 19:05
  • 2
    in .overlay {} you wrote top:0 twice ;) – Florian Boudot Nov 21 '17 at 15:51
  • @Rantiev it will not jump to the top because of `overflow:hidden` it will jump with `position:fixed` which people usually add to this solution – godblessstrawberry Feb 27 '18 at 07:33
  • @godblessstrawberry could you provide an example? Before writing my comment (and before comment itself had such situations) i was checking this. If you add overflow: hidden to any element (regardless of position property value) it loses it's scroll and therefore loses it's scroll position (which you will need to restore by JS if you want to make it scrollable as make scroll to be in the old place again). Could you please prove it, might things changed, or you check it in specific browser? Will appreciate demo (jsfiddle jsbin or anything), tx – Rantiev Feb 27 '18 at 17:08
  • Igor Alemasow has the correct answer. CSS overscroll-behavior. No JavaScript necessary – joseph.l.hunsaker Aug 24 '18 at 05:14
  • @joseph it's worth noting that the only js I wrote is only nedeed to open and close the overlay. – Fabrizio Calderan loves trees Aug 24 '18 at 06:15
  • 1
    nice answer. Also it was at 666 upvotes for a long time, glad I can bump it to 667 for a better number (just superstition) anyway - thanks for a great solution! – Gel Feb 19 '21 at 14:34
82

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

body.noscroll{
    position:fixed;
    overflow:hidden;
}
Toosick
  • 450
  • 1
  • 6
  • 14
am80l
  • 1,425
  • 12
  • 11
  • 115
    With this solution, because of the fixed position, the body will automatically scroll to the top of your content. This could be disturbing for your users. – tzi Jul 02 '14 at 12:21
  • 5
    Another problem with using `position:fixed` is that it was resizing my main body. Maybe it was conflicting with other CSS, but `overflow:hidden` is all that was needed – Dex Jul 17 '14 at 19:56
  • 3
    this is breaking focus jumping when you tab through input fields – Atav32 Jul 17 '15 at 18:42
  • @Atav32 Sounds like a seperate issue, position and overflow should not be effecting tab order. – am80l Aug 26 '15 at 09:18
  • @am80l sorry my previous comment was super vague: the tab order works find, but the screen jolts when you tab through fields on iOS Safari (http://stackoverflow.com/questions/31126330/tabbing-through-input-fields-in-mobile-safari-makes-browser-jump) – Atav32 Aug 26 '15 at 17:19
  • `overflow: hidden` won't work (on it's own) if you've scrolled the page and then trigger the modal. You'll need the `position: fixed` there – FloatingRock Jun 15 '17 at 11:35
  • Hahaha. I opened the codepen and their was a CodePen overlay asking to signup an account. I wasn't able to scroll the background. Thats such a coincidence. – JBis Sep 10 '18 at 00:34
  • I just set the `position: fixed`, this was set to absolute. The overlay now covers the whole screen even with scrolling. Do i need to add overflow: hidden? – tony2tones Mar 02 '19 at 14:25
58

overscroll-behavior css property allows to override the browser's default overflow scroll behavior when reaching the top/bottom of content.

Just add the following styles to overlay:

.overlay {
   overscroll-behavior: contain;
   ...
}

Codepen demo

Currently works in Chrome, Firefox and IE(caniuse)

For more details check google developers article.

Igor Alemasow
  • 3,424
  • 1
  • 20
  • 21
  • 4
    I have come all the way to tell you that this is working on latest Chrome, Mozilla and Opera. Have a wonderful time! – Wannabe JavaGeek Mar 06 '18 at 19:44
  • 3
    Someone should add a bounty on this. This is the correct solution. No JavaScript necessary. :) – joseph.l.hunsaker Aug 24 '18 at 05:14
  • 19
    Problem with this solution is that it only works if overlay is scrollable. If you have a modal and it all fit on screen so there is no scroll inside — it will not stop the body scroll. That and it also doesn't work in Safari at all :) – waterplea Mar 15 '19 at 07:03
  • 1
    I settled with a combination of this with `overflow-y: scroll` (from your Codepen demo). The point raised by @pokrishka is valid, but in my case it's not a concern. In any case, I wonder if the browsers' implementations are going to cover this detail in the future. I found that, in Firefox, resizing the browser so that the modal does not fit the screen and then resizing it again so that it does makes this property work (i.e. scroll is contained) even after resizing to full size - until the page is reloaded, at least. – Marc.2377 May 02 '19 at 17:15
50

Don't use overflow: hidden; on body. It automatically scrolls everything to the top. There's no need for JavaScript either. Make use of overflow: auto;. This solution even works with mobile Safari:

HTML Structure

<div class="overlay">
    <div class="overlay-content"></div>
</div>

<div class="background-content">
    lengthy content here
</div>

Styling

.overlay{
    position: fixed;
    top: 0px;
    left: 0px;
    right: 0px;
    bottom: 0px;
    background-color: rgba(0, 0, 0, 0.8);

    .overlay-content {
        height: 100%;
        overflow: scroll;
    }
}

.background-content{
    height: 100%;
    overflow: auto;
}

See the demo here and source code here.

Update:

For people who want keyboard space bar, page up/down to work: you need to focus on the overlay, e.g., clicking on it, or manually JS focusing on it before this part of the div will respond to keyboard. Same with when the overlay is "switched off", since it's just moving the overlay to the side. Otherwise to browser, these are just two normal divs and it wouldn't know why it should focus on any one of them.

Lucia
  • 11,183
  • 6
  • 34
  • 46
  • 5
    cool solution, but then i need to wrap all of my content in a div, which i had no intention of doing... – Jacob Raccuia Sep 03 '15 at 00:07
  • 2
    This is an ideal solution. If anyone is having trouble with this, you might need to add `html, body { height: 100%; }` (as in the demo) for this to work correctly. – John Sep 18 '15 at 16:07
  • Great idea though you still use a heavy library in the back (AngularJS) to make it work so it's not really answering the question fully, though it's cool to this in action. – user18490 Sep 23 '15 at 21:26
  • 4
    @user18490 the angular/material part has nothing to do with the fact that this solution is working. – Lucia Sep 24 '15 at 15:22
  • 1
    @Luxiyalu: yes I got that and yet your example (link) uses that framework so anyone who wants to replicate your example simply can't do it by a simple copy/paste of your code. The point of Stackoverflow is to have complete/functional examples that runs out of any lib dependencies. – user18490 Sep 25 '15 at 08:38
  • not sure this works. at the top/end of the overlay, you will be able to continue to scroll the background. – Rex Dec 10 '15 at 11:51
  • 11
    This breaks mobile devices UX in a number of ways (ie: URL-bar Hiding, Overscroll Affordance, ...), even the *spacebar to scroll* on desktops. – nitely Dec 14 '15 at 20:20
  • 2
    This is the more robust option, but it does complicate things a bit. For instance, PageUp and PageDown won't work on refresh. anything that uses the `.offset()` values for calculation gets messed up, etc... – BlackPanther Jul 27 '16 at 18:05
  • Works with edge but not with previous explorers. – Iggy Jun 03 '17 at 13:56
  • And once you close your modal, you will see your page has been scrolled up. – Jimba Tamang Jun 21 '17 at 01:26
  • Set background-color: rgba(0, 0, 0, 0.1); instead of 0.8 for the alpha and you will never notice it. – AndroidDev Jun 26 '17 at 16:45
  • Good idea, but unfortunately no reliable solution. This breaks a number of important UX functions e.g. iOS (v.10) smoothscrolling. – Jonathan Arbely Jan 30 '18 at 14:37
  • Unfortunately, if you're using `-webkit-overflow-scrolling: touch` on the background content, scrolling is still possible. I believe it's because it invalidates the `overflow: auto` in some way. Also, this solution will technically prevent scrolling as described, but if your modal doesn't span the entire page, the content will still be movable slightly in iOS—only it will snap back to position after releasing. I've found that [this](https://stackoverflow.com/a/45230674/1533191) solution addresses these issues. – CLL Feb 17 '18 at 22:32
  • With this solution, when the overlay exposes some of the background, the background can still be scrolled (overlay{top: 60px;...). This use case is not uncommon, as one might want to code a menu dropdown that freezes the scrolling of the body. – zumafra Dec 04 '19 at 16:06
  • @zumafra If you need to freeze the whole page, you could always set the overlay div as a backdrop that covers the whole page, then style another child div to be your visual dropdown. – Lucia Dec 05 '19 at 20:53
42

Most solutions have the problem that they do not retain the scroll position, so I took a look at how Facebook does it. In addition to setting the underlaying content to position: fixed they also set the top dynamically to retain the scroll position:

scrollPosition = window.pageYOffset;
mainEl.style.top = -scrollPosition + 'px';

Then, when you remove the overlay again, you need to reset the scroll position:

window.scrollTo(0, scrollPosition);

I created a little example to demonstrate this solution

let overlayShown = false;
let scrollPosition = 0;

document.querySelector('.toggle').addEventListener('click', function() {
  if (overlayShown) {
  showOverlay();
  } else {
    removeOverlay();
  }
  overlayShown = !overlayShown;
});

function showOverlay() {
    scrollPosition = window.pageYOffset;
   const mainEl = document.querySelector('.main-content');
    mainEl.style.top = -scrollPosition + 'px';
   document.body.classList.add('show-overlay');
}

function removeOverlay() {
  document.body.classList.remove('show-overlay');
   window.scrollTo(0, scrollPosition);
    const mainEl = document.querySelector('.main-content');
    mainEl.style.top = 0;
}
.main-content {
  background-image: repeating-linear-gradient( lime, blue 103px);
  width: 100%;
  height: 200vh;
}

.show-overlay .main-content {
  position: fixed;
  left: 0;
  right: 0;
  overflow-y: scroll; /* render disabled scroll bar to keep the same width */
}

.overlay {
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.3);
  overflow: auto;
}

.show-overlay .overlay {
  display: block;
}

.overlay-content {
  margin: 50px;
  background-image: repeating-linear-gradient( grey, grey 20px, black 20px, black 40px);
  height: 120vh;
}

.toggle {
  position: fixed;
  top: 5px;
  left: 15px;
  padding: 10px;
  background: red;
}

/* reset CSS */
body {
  margin: 0;
}
<main class="main-content"></main>

  <div class="overlay">
    <div class="overlay-content"></div>
  </div>
  
  <button class="toggle">Overlay</button>
Philipp Mitterer
  • 643
  • 8
  • 15
  • This seems to be the most elegant solution to me. I know Google employs the same tricks of scrolling the page and moving elements often. – forallepsilon Jul 03 '18 at 19:41
  • This is the one and only solution that worked 100% correct for me, I wish I found this answer earlier. – user0103 Jul 16 '19 at 18:24
31

It is worth noting that sometimes adding "overflow:hidden" to the body tag doesn't do the job. In those cases, you'll have to add the property to the html tag as well.

html, body {
    overflow: hidden;
}
Francisco Hodge
  • 1,407
  • 2
  • 18
  • 27
  • For iOS, you need to also set `width: 100%; height: 100%;` – Gavin Nov 24 '16 at 16:57
  • 2
    This doesn't address a problem also seen in many of the other solutions for this question—if the window is scrolled anywhere but the top of the page when the modal is open, it will cause a scroll to the top of the page. I found Philipp Mitterer's solution [here](https://stackoverflow.com/a/45230674/1533191) to be the best option that covers most cases. – CLL Feb 17 '18 at 21:58
  • 1
    I have never experienced this issue you speak of (on either web or mobile). Could you share the full code (with DOM) that doesn't work in a fiddle or jsbin? In any case, I am wary of using Javascript workarounds to tackle this issue. – Francisco Hodge Mar 19 '18 at 20:49
  • It break any Position: sticky in whole page. Be carefull! Also break any width/height required size as they uknown in some situation (swiper or flex style i particular) – Patryk Padus Jan 26 '21 at 22:50
7

Generally speaking, if you want a parent (the body in this case) to prevent it from scrolling when a child (the overlay in this case) scrolls, then make the child a sibling of the parent to prevent the scroll event from bubbling up to the parent. In case of the parent being the body, this requires an additional wrapping element:

<div id="content">
</div>
<div id="overlay">
</div>

See Scroll particular DIV contents with browser's main scrollbar to see its working.

Community
  • 1
  • 1
NGLN
  • 41,230
  • 8
  • 102
  • 186
  • 1
    Best solution, true "out of the box" thinking, just not so well phrased. – Mark May 04 '16 at 06:01
  • If the body isn't the element that scrolls, than topbars won't slide up on mobile as you scroll down into the page. – Seph Reed Sep 27 '19 at 20:40
7

The chosen answer is correct, but has some limitations:

  • Super hard "flings" with your finger will still scroll <body> in the background
  • Opening the virtual keyboard by tapping an <input> in the modal will direct all future scrolls to <body>

I don't have a fix for the first issue, but wanted to shed some light on the second. Confusingly, Bootstrap used to have the keyboard issue documented, but they claimed it was fixed, citing http://output.jsbin.com/cacido/quiet as an example of the fix.

Indeed, that example works fine on iOS with my tests. However, upgrading it to the latest Bootstrap (v4) breaks it.

In an attempt to figure out what the difference between them was, I reduced a test case to no longer depend on Bootstrap, http://codepen.io/WestonThayer/pen/bgZxBG.

The deciding factors are bizarre. Avoiding the keyboard issue seems to require that background-color is not set on the root <div> containing the modal and the modal's content must be nested in another <div>, which can have background-color set.

To test it, uncomment the below line in the Codepen example:

.modal {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 2;
  display: none;
  overflow: hidden;
  -webkit-overflow-scrolling: touch;
  /* UNCOMMENT TO BREAK */
/*   background-color: white; */
}
Weston
  • 1,782
  • 1
  • 18
  • 26
4

I found this question trying to solve issue I had with my page on Ipad and Iphone - body was scrolling when I was displaying fixed div as popup with image.

Some answers are good, however none of them solved my issue. I found following blog post by Christoffer Pettersson. Solution presented there helped issue I had with iOS devices and it helped my scrolling background problem.

Six things I learnt about iOS Safari's rubber band scrolling

As it was suggested I include major points of the blog post in case link gets outdated.

"In order to disable that the user can scroll the background page while the "menu is open", it is possible to control what elements should be allowed to be scrolled or not, by applying some JavaScript and a CSS class.

Based on this Stackoverflow answer you can control that elements with the disable-scrolling should not perform their default scroll action when the touchmove event is triggered."

 document.ontouchmove = function ( event ) {

    var isTouchMoveAllowed = true, target = event.target;

    while ( target !== null ) {
        if ( target.classList && target.classList.contains( 'disable-scrolling' ) ) {
            isTouchMoveAllowed = false;
            break;
        }
        target = target.parentNode;
    }

    if ( !isTouchMoveAllowed ) {
        event.preventDefault();
    }
};

And then put the disable-scrolling class on the page div:

<div class="page disable-scrolling">
kvetis
  • 4,608
  • 1
  • 20
  • 37
TheFullResolution
  • 1,091
  • 1
  • 16
  • 25
4

For touch devices, try adding a 1px wide, 101vh min-height transparent div in the wrapper of the overlay. Then add -webkit-overflow-scrolling:touch; overflow-y: auto; to the wrapper. This tricks mobile safari into thinking the overlay is scrollable, thus intercepting the touch event from the body.

Here's a sample page. Open on mobile safari: http://www.originalfunction.com/overlay.html

https://gist.github.com/YarGnawh/90e0647f21b5fa78d2f678909673507f

YarGnawh
  • 3,970
  • 6
  • 20
  • 35
4

The behaviour you want to prevent is called scroll chaining. To disable it, set

overscroll-behavior: contain;

on your overlay in CSS.

黄雨伞
  • 1,588
  • 1
  • 13
  • 14
4

You can easily do this with some "new" css and JQuery.

Initially: body {... overflow:auto;} With JQuery you can dynamically switch between 'overlay' and 'body'. When on 'body', use

body {
   position: static;
   overflow: auto;
}

When on 'overlay' use

body {
   position: sticky;
   overflow: hidden;
}

JQuery for the switch('body'->'overlay'):

$("body").css({"position": "sticky", "overflow": "hidden"});

JQuery for the switch('overlay'->'body'):

$("body").css({"position": "static", "overflow": "auto"});
christk
  • 495
  • 5
  • 15
2

If the intent is to disable on mobile/ touch devices then the most straightforward way to do it is using touch-action: none;.

Example:

const app = document.getElementById('app');
const overlay = document.getElementById('overlay');

let body = '';

for (let index = 0; index < 500; index++) {
  body += index + '<br />';
}

app.innerHTML = body;
app.scrollTop = 200;

overlay.innerHTML = body;
* {
  margin: 0;
  padding: 0;
}

html,
body {
  height: 100%;
}

#app {
  background: #f00;
  position: absolute;
  height: 100%;
  width: 100%;
  overflow-y: scroll;
  line-height: 20px;
}

#overlay {
  background: rgba(0,0,0,.5);
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 100%;
  padding: 0 0 0 100px;
  overflow: scroll;
}
<div id='app'></div>
<div id='overlay'></div>

(The example does not work in the context of Stack Overflow. You will need to recreate it in a stand-alone page.)

If you want to disable scrolling of the #app container, just add touch-action: none;.

Gajus
  • 55,791
  • 58
  • 236
  • 384
  • 1
    If touch action actually worked on iOS that is. And in that case there'd be no need to wrap your entire "app" in a separate div. – powerbuoy Dec 05 '17 at 18:57
1

I'd like to add to previous answers because I tried to do that, and some layout broke as soon as I switched the body to position:fixed. In order to avoid that, I had to also set body's height to 100% :

function onMouseOverOverlay(over){
    document.getElementsByTagName("body")[0].style.overflowY = (over?"hidden":"scroll");
    document.getElementsByTagName("html")[0].style.position = (over?"fixed":"static");
    document.getElementsByTagName("html")[0].style.height = (over?"100%":"auto");
}
Vic Seedoubleyew
  • 9,705
  • 6
  • 40
  • 61
1

Use the following HTML:

<body>
  <div class="page">Page content here</div>
  <div class="overlay"></div>
</body>

Then JavaScript to intercept and stop scrolling:

$(".page").on("touchmove", function(event) {
  event.preventDefault()
});

Then to get things back to normal:

$(".page").off("touchmove");

Damir Kotoric
  • 853
  • 6
  • 8
0

try this

var mywindow = $('body'), navbarCollap = $('.navbar-collapse');    
navbarCollap.on('show.bs.collapse', function(x) {
                mywindow.css({visibility: 'hidden'});
                $('body').attr("scroll","no").attr("style", "overflow: hidden");
            });
            navbarCollap.on('hide.bs.collapse', function(x) {
                mywindow.css({visibility: 'visible'});
                $('body').attr("scroll","yes").attr("style", "");
            });
Junaid
  • 3,203
  • 1
  • 23
  • 31
-1

In my case, none of these solutions worked out on iPhone (iOS 11.0).

The only effective fix that is working on all my devices is this one - ios-10-safari-prevent-scrolling-behind-a-fixed-overlay-and-maintain-scroll-position

potar
  • 388
  • 4
  • 5
-1

If you want to stop body/html scroll add as the following

CSS

    html, body {
        height: 100%;
    }

    .overlay{
        position: fixed;
        top: 0px;
        left: 0px;
        right: 0px;
        bottom: 0px;
        background-color: rgba(0, 0, 0, 0.8);

        .overlay-content {
            height: 100%;
            overflow: scroll;
        }
    }

    .background-content{
        height: 100%;
        overflow: auto;
    }

HTML

    <div class="overlay">
        <div class="overlay-content"></div>
    </div>

    <div class="background-content">
        lengthy content here
    </div>

Basically, you could do it without JS.

The main idea is to add html/body with height: 100% and overflow: auto. and inside your overlay, you could either enable/disable scroll based on your requirement.

Hope this helps!

Ashwin
  • 524
  • 4
  • 12
-3

Use below code for disabling and enabling scroll bar.

Scroll = (
    function(){
          var x,y;
         function hndlr(){
            window.scrollTo(x,y);
            //return;
          }  
          return {

               disable : function(x1,y1){
                    x = x1;
                    y = y1;
                   if(window.addEventListener){
                       window.addEventListener("scroll",hndlr);
                   } 
                   else{
                        window.attachEvent("onscroll", hndlr);
                   }     

               },
               enable: function(){
                      if(window.removeEventListener){
                         window.removeEventListener("scroll",hndlr);
                      }
                      else{
                        window.detachEvent("onscroll", hndlr);
                      }
               } 

          }
    })();
 //for disabled scroll bar.
Scroll.disable(0,document.body.scrollTop);
//for enabled scroll bar.
Scroll.enable();
Rakesh Chaudhari
  • 2,420
  • 1
  • 21
  • 22