Prevent BODY from scrolling when a modal is opened
Asked Answered
C

54

437

I want my body to stop scrolling when using the mousewheel while the Modal (from http://twitter.github.com/bootstrap) on my website is opened.

I've tried to call the piece of javascript below when the modal is opened but without success

$(window).scroll(function() { return false; });

AND

$(window).live('scroll', function() { return false; });

Please note our website dropped support for IE6, IE7+ needs to be compatible though.

Centerboard answered 2/3, 2012 at 19:0 Comment(0)
C
540

Bootstrap's modal automatically adds the class modal-open to the body when a modal dialog is shown and removes it when the dialog is hidden. You can therefore add the following to your CSS:

body.modal-open {
    overflow: hidden;
}

You could argue that the code above belongs to the Bootstrap CSS code base, but this is an easy fix to add it to your site.

Update 8th feb, 2013
This has now stopped working in Twitter Bootstrap v. 2.3.0 -- they no longer add the modal-open class to the body.

A workaround would be to add the class to the body when the modal is about to be shown, and remove it when the modal is closed:

$("#myModal").on("show", function () {
  $("body").addClass("modal-open");
}).on("hidden", function () {
  $("body").removeClass("modal-open")
});

Update 11th march, 2013 Looks like the modal-open class will return in Bootstrap 3.0, explicitly for the purpose of preventing the scroll:

Reintroduces .modal-open on the body (so we can nuke the scroll there)

See this: https://github.com/twitter/bootstrap/pull/6342 - look at the Modal section.

Cenac answered 13/6, 2012 at 11:33 Comment(21)
this does not work anymore in bootstrap 2.2.2. Hopefully .modal-open will come back in the future... github.com/twitter/bootstrap/issues/5719Onionskin
@Onionskin I don't expect it to return. Based on this writing: github.com/twitter/bootstrap/wiki/Upcoming-3.0-changes (look at the bottom). Quote: "No more inner modal scrolling. Instead, modals will grow to house their content and the page scroll to house the modal."Cenac
@Cenac yeah i know, today the bootstrap team closed the github issue as 'wontfix'.Onionskin
And the modal-open class will be back to prevent scrolling in Bootstrap 3.0! See my update.Cenac
@anand at the time the current answer was the best answer, after an x amount of time Icannot change the answer anymore.Centerboard
Worked for me. Just used $('body').addClass('modal-open') before $('#modal').modal('show') and $('body').removeClass('modal-open') before $('#modal').modal('hide'). I have put the CSS code too. Thanks a lot!Overcautious
@Overcautious Cool - the modal-open will return in Bootstrap 3, so when that launches it should be safe to remove the above code.Cenac
This is why one should keep scrolling down, if the chosen answer doesn't satisfy you, there are chances that you are gonna find gems like these. didnt expect a 97+ voted answer burried so deep under other less liked comments.Kenyakenyatta
Hi @wardha-Web It is only under the accepted answer, as far as I see it. The ranking must be by answer, then by votes. But yes, accepted answers are not always the best :)Cenac
anyway to stop the page from shifting to the right when a modal is open?Mattern
@Mattern You can use the modal-open class on the body tag again, and set padding-right: 17px; - but this is not safe. It shifts the width of the scrollbar which will vary for browser/OS. Also, if the page does not have scroll bars before opening the modal, the padding should not be added.Cenac
@Mattern - I just solved the shifting issue (which is IMO very annoying) with the following CSS code: body { padding-top: 70px; overflow-y: scroll; } body.modal-open { overflow-y: scroll; } .modal { overflow: auto; } ... With this the vertical scrolls will be always visible on body, and the modal scroll will be visible only when needed.Bombe
Chrome for Android just didn't like this solution! I had to go with position fixed, as Brad outlined below. YMMV, of course :)Ranitta
For some reason pinterest modal dialog is not scrolled. And they use noScroll{overflow:hidden;} on the body and body has position:relative that is never changed to fixed.Dubitation
the problem with this is that to prevent the document from scrolling to the top when a modal is opened you needed to add body.modal-open{overflow:visible}. Your solution works at the moment, with the downside that the document scrolls to the top once a modal is openenGog
@Gog With 3.0 and newer, it all works as it should out of the box. No need to manually add the modal-open class to the body. It keeps the scroll position of body.Cenac
@MartinHN, no, it doesn't... that's why I was looking here for a solution. Might be a combination with something else, but the standard modal caused the original page to scroll to the top once a modal was openedGog
@Cenac Unfortunately not worked for me with Bootstrap 3. On the other hand normally I use html { overflow-y: scroll; } on my page body and when modal is open the scrollbar gets twice. Any idea?Malloch
It prevents from scrolling, but cannot stop the scroll events firing. Checked in Mac OS X Chrome version 49.Indetermination
Although the background is keep scrolling to the bottom, I make it scroll to the top after closing the modal by using JQuery. $(window).scrollTop(0);Invaginate
Nice one, now I know how to use Inspect Element to get rid of these paywall modals on news sites and make scrolling working again :) CheersVernon
P
144

The accepted answer doesn't work on mobile (iOS 7 w/ Safari 7, at least) and I don't want MOAR JavaScript running on my site when CSS will do.

This CSS will prevent the background page from scrolling under the modal:

body.modal-open {
    overflow: hidden;
    position: fixed;
}

However, it also has a slight side-affect of essentially scrolling to the top. position:absolute resolves this but, re-introduces the ability to scroll on mobile.

If you know your viewport (my plugin for adding viewport to the <body>) you can just add a css toggle for the position.

body.modal-open {
    /* block scroll for mobile; */
    /* causes underlying page to jump to top; */
    /* prevents scrolling on all screens */
    overflow: hidden;
    position: fixed;
}
body.viewport-lg {
    /* block scroll for desktop; */
    /* will not jump to top; */
    /* will not prevent scroll on mobile */
    position: absolute; 
}

I also add this to prevent the underlying page from jumping left/right when showing/hiding modals.

body {
    /* STOP MOVING AROUND! */
    overflow-x: hidden;
    overflow-y: scroll !important;
}

this answer is a x-post.

Parodic answered 13/7, 2014 at 21:39 Comment(5)
Thanks! Mobiles (at least iOS and Android native browser) were giving me a headache and wouldn't work without the position: fixed on the body.Crowther
Thanks for also including the code for preventing the page from jumping left/right when showing/hiding modals. This was extremely useful and I verified it fixes an issue I was having on Safari on an iPhone 5c running iOS 9.2.1.Dishwater
In order to fix scrolling to top, you can record position before adding the class, then after class is removed, do window.scroll to recorded position. This is how I fixed it for myself: pastebin.com/Vpsz07zd.Culprit
Though I feel this would only fit very basic needs, it was a useful fix. Thanks.Seriocomic
Here's how I did it to retain the scroll position with jquery: js const currPageScrollPos = $(window).scrollTop() $("body").removeClass("show_overlay") $(window).delay(5).scrollTop(currPageScrollPos) Jeuz
C
39

Simply hide the body overflow and it makes body not scrolling. When you hide the modal, revert it to automatic.

Here is the code:

$('#adminModal').modal().on('shown', function(){
    $('body').css('overflow', 'hidden');
}).on('hidden', function(){
    $('body').css('overflow', 'auto');
})
Cinnamon answered 14/11, 2012 at 23:1 Comment(0)
A
25

You need to go beyond @charlietfl's answer and account for scrollbars, otherwise you may see a document reflow.

Opening the modal:

  1. Record the body width
  2. Set body overflow to hidden
  3. Explicitly set the body width to what it was in step 1.

    var $body = $(document.body);
    var oldWidth = $body.innerWidth();
    $body.css("overflow", "hidden");
    $body.width(oldWidth);
    

Closing the modal:

  1. Set body overflow to auto
  2. Set body width to auto

    var $body = $(document.body);
    $body.css("overflow", "auto");
    $body.width("auto");
    

Inspired by: http://jdsharp.us/jQuery/minute/calculate-scrollbar-width.php

Allomorphism answered 8/5, 2013 at 23:20 Comment(5)
I was getting a "jumpy" screen for lack of a better term and the key here for me was following the width settings. Thanks a ton.Uncurl
@Uncurl Yep: "jumpy" == "document reflow". Glad it helped you! :-)Allomorphism
is there a css solution for this? does it work in all browsers and mobile?Reste
just a typo: It must be $body.css("overflow", "auto"); in the last step :)Vertebral
Oh man, what an marvelous idea. @Allomorphism you helped me resolve the document reflow issue that was bugging me for a long time.Jena
P
21

You could try setting body size to window size with overflow: hidden when modal is open

Petiole answered 2/3, 2012 at 19:5 Comment(7)
doing so would move the screen every time the modal is opened, that is not convenient, I just need to disable the background scrollCenterboard
not sure that you can scrollbars are browser related and you can't do much with themPetiole
you could set modal to "fixed" so won't move when scrollingPetiole
its not about stopping the modal from scrolling, its about stopping the body in the background from scrollingCenterboard
it's the window that scrolls if body overflows it. For example the scrollbars are not part of document.Petiole
your initial answer, I just didn't realize it at the timeCenterboard
Great solution; you just need to account for scrollbars to avoid a document reflow and content "jump". See my answer.Allomorphism
R
18

Warning: The option below is not relevant to Bootstrap v3.0.x, as scrolling in those versions has been explicitly confined to the modal itself. If you disable wheel events you may inadvertently prevent some users from viewing the content in modals that have heights greater than the viewport height.


Yet Another Option: Wheel Events

The scroll event is not cancelable. However it is possible to cancel the mousewheel and wheel events. The big caveat is that not all legacy browsers support them, Mozilla only recently adding support for the latter in Gecko 17.0. I don't know the full spread, but IE6+ and Chrome do support them.

Here's how to leverage them:

$('#myModal')
  .on('shown', function () {
    $('body').on('wheel.modal mousewheel.modal', function () {
      return false;
    });
  })
  .on('hidden', function () {
    $('body').off('wheel.modal mousewheel.modal');
  });

JSFiddle

Rabia answered 29/11, 2012 at 3:25 Comment(5)
Your JSFiddle doesn't work on Firefox 24, Chrome 24 nor on IE9.Haematothermal
@Haematothermal should be working now. The only issue was that the external references to the Bootstrap library had changed. Thanks for the heads up.Rabia
This doesn't prevent touch devices from scrolling, the overflow:hidden is better for now.Hewlett
they are all working but not for mobile devices. -__- Tested Android 4.1 and ChromeSpill
@TowerJimmy you probably need to cancel out the touch or drag events for that (if that's even possible)Centerboard
V
12

Try this one:

.modal-open {
    overflow: hidden;
    position:fixed;
    width: 100%;
    height: 100%;
}

it worked for me. (supports IE8)

Valiancy answered 15/2, 2015 at 11:48 Comment(1)
This works but also causes you to jump to the top when you open a modal when using position: fixed.Rubella
O
11

As of November 2017 Chrome Introduced a new css property

overscroll-behavior: contain;

which solves this problem although as of writing has limited cross browser support.

see below links for full details and browser support

Outstretched answered 3/4, 2018 at 2:6 Comment(3)
Even in 2020 caniuse puts support of this at only 78%. Hardly a viable solution.Jocund
In 2022 it seems to get better: caniuse.com/?search=overscroll-behaviorJacquelyn
Works good on desktop. Caveats I've noticed (2022 Dec 7). Doesn't work on desktop. Doesn't work for non-scroll-able divs (think divs with overflow-y:auto which may or may not be scroll-able at any given pointKimberlykimberlyn
R
9
/* =============================
 * Disable / Enable Page Scroll
 * when Bootstrap Modals are
 * shown / hidden
 * ============================= */

function preventDefault(e) {
  e = e || window.event;
  if (e.preventDefault)
      e.preventDefault();
  e.returnValue = false;  
}

function theMouseWheel(e) {
  preventDefault(e);
}

function disable_scroll() {
  if (window.addEventListener) {
      window.addEventListener('DOMMouseScroll', theMouseWheel, false);
  }
  window.onmousewheel = document.onmousewheel = theMouseWheel;
}

function enable_scroll() {
    if (window.removeEventListener) {
        window.removeEventListener('DOMMouseScroll', theMouseWheel, false);
    }
    window.onmousewheel = document.onmousewheel = null;  
}

$(function () {
  // disable page scrolling when modal is shown
  $(".modal").on('show', function () { disable_scroll(); });
  // enable page scrolling when modal is hidden
  $(".modal").on('hide', function () { enable_scroll(); });
});
Rameses answered 22/5, 2013 at 22:38 Comment(0)
C
9

Couldn't make it work on Chrome just by changing CSS, because I didn't want the page to scroll back to the top. This worked fine:

$("#myModal").on("show.bs.modal", function () {
  var top = $("body").scrollTop(); $("body").css('position','fixed').css('overflow','hidden').css('top',-top).css('width','100%').css('height',top+5000);
}).on("hide.bs.modal", function () {
  var top = $("body").position().top; $("body").css('position','relative').css('overflow','auto').css('top',0).scrollTop(-top);
});
Conditioned answered 29/10, 2013 at 20:1 Comment(3)
this is the only solution that worked for me on bootstrap 3.2.Arnaldo
Had the jump to top issue with angular-material sidenavs. This was the only answer that seems to work in all cases (desktop/mobile + allow scroll in the modal/sidenav + leave the body scroll position fixed without jumping).Temptress
This is also only solution that actually worked for me. txDebt
E
9

Adding the class 'is-modal-open' or modifying style of body tag with javascript is okay and it will work as supposed to. But the problem we gonna face is when the body becomes overflow:hidden, it will jump to the top, ( scrollTop will become 0 ). This will become a usability issue later.

As a solution for this problem, instead of changing body tag overflow:hidden change it on html tag

$('#myModal').on('shown.bs.modal', function () {
  $('html').css('overflow','hidden');
}).on('hidden.bs.modal', function() {
  $('html').css('overflow','auto');
});
Eveevection answered 28/8, 2016 at 8:6 Comment(1)
This is absolutely the correct answer because the general consensus answer scrolls the page to the top which is a horrible user experience. You should write a blog on this. I ended up turning this into a global solution.Malfunction
S
8

For Bootstrap, you might try this (working on Firefox, Chrome and Microsoft Edge) :

body.modal-open {
    overflow: hidden;
    position:fixed;
    width: 100%;
}
Suber answered 22/9, 2016 at 18:52 Comment(0)
F
8

It's 2022, and there are better CSS solutions now. This works well. You can add this class to the body element, or modify the body style itself, when the modal is open.

  .body-no-scroll {
    height: 100vh;
    width: 100vw;
    touch-action: none;
    -webkit-overflow-scrolling: none;
    overflow: hidden;
    overscroll-behavior: none;
  }
Foolproof answered 10/8, 2022 at 1:11 Comment(1)
This answer definitely needs more upvotes. Can confirm it works well.Turnstone
A
7

React , if you are looking for

useEffect in the modal that is getting popedup

 useEffect(() => {
    document.body.style.overflowY = 'hidden';
    return () =>{
      document.body.style.overflowY = 'auto';
    }
  }, [])
Angelaangele answered 5/2, 2021 at 9:14 Comment(0)
K
5

I'm not sure about this code, but it's worth a shot.

In jQuery:

$(document).ready(function() {
    $(/* Put in your "onModalDisplay" here */)./* whatever */(function() {
        $("#Modal").css("overflow", "hidden");
    });
});

As I said before, I'm not 100% sure but try anyway.

Kenneth answered 2/3, 2012 at 19:26 Comment(4)
This doesn't prevents the body from scrolling while the modal is openedCenterboard
@Centerboard you stated in the comments section of charlietfl's answer that this works. But you said: This doesn't prevents the body from scrolling while the modal is opened.Kenneth
true but for now its the only solution that partly works and I'm fine with. the reason why I marked his response as answer is because he was earlier with the answer.Centerboard
@Centerboard really, i thought i answered before him/her , might be some weird glitch. its fine thoughKenneth
U
5

I'm not 100% sure this will work with Bootstrap but worth a try - it worked with Remodal.js which can be found on github: http://vodkabears.github.io/remodal/ and it would make sense for the methods to be pretty similar.

To stop the page jumping to the top and also prevent the right shift of content add a class to the body when the modal is fired and set these CSS rules:

body.with-modal {
    position: static;
    height: auto;
    overflow-y: hidden;
}

It's the position:static and the height:auto that combine to stop the jumping of content to the right. The overflow-y:hidden; stops the page from being scrollable behind the modal.

Ultrasonic answered 3/7, 2015 at 10:39 Comment(0)
M
3

The best solution is the css-only body{overflow:hidden} solution used by most of these answers. Some answers provide a fix that also prevent the "jump" caused by the disappearing scrollbar; however, none were too elegant. So, I wrote these two functions, and they seem to work pretty well.

var $body = $(window.document.body);

function bodyFreezeScroll() {
    var bodyWidth = $body.innerWidth();
    $body.css('overflow', 'hidden');
    $body.css('marginRight', ($body.css('marginRight') ? '+=' : '') + ($body.innerWidth() - bodyWidth))
}

function bodyUnfreezeScroll() {
    var bodyWidth = $body.innerWidth();
    $body.css('marginRight', '-=' + (bodyWidth - $body.innerWidth()))
    $body.css('overflow', 'auto');
}

Check out this jsFiddle to see it in use.

Manvell answered 29/8, 2013 at 2:55 Comment(5)
This is not so old but none of this is working at all for me not even the fiddle I can still scroll the window, what am I missing ?Glennaglennie
Open and Close the modal a few times, and the main green DIV shrinks and shrinks!Hewlett
@Webinan I'm not seeing this in Chrome on Win7. What's your browser and viewport size?Manvell
@smhmic after about 20 times of open and close the green div's width becomes equal to the open modal buttons' width. Chrome on 8Hewlett
Similar to @jpap's answer (https://mcmap.net/q/57056/-prevent-body-from-scrolling-when-a-modal-is-opened) but with added bugs. :-(Nehru
E
3

Many suggest "overflow: hidden" on the body, which will not work (not in my case at least), since it will make the website scroll to the top.

This is the solution that works for me (both on mobile phones and computers), using jQuery:

    $('.yourModalDiv').bind('mouseenter touchstart', function(e) {
        var current = $(window).scrollTop();
        $(window).scroll(function(event) {
            $(window).scrollTop(current);
        });
    });
    $('.yourModalDiv').bind('mouseleave touchend', function(e) {
        $(window).off('scroll');
    });

This will make the scrolling of the modal to work, and prevent the website from scrolling at the same time.

Evvie answered 18/9, 2017 at 8:57 Comment(1)
Looks like this solution fixes this iOS bug too: hackernoon.com/…Soapstone
P
3

I had to set the viewport-height to get this working perfectly

body.modal-open {
  height: 100vh;
  overflow: hidden;
}
Pedicure answered 20/4, 2021 at 11:7 Comment(0)
Y
2

You could use the following logic, I tested it and it works(even in IE)

   <html>

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<script type="text/javascript">

var currentScroll=0;
function lockscroll(){
    $(window).scrollTop(currentScroll);
}


$(function(){

        $('#locker').click(function(){
            currentScroll=$(window).scrollTop();
            $(window).bind('scroll',lockscroll);

        })  


        $('#unlocker').click(function(){
            currentScroll=$(window).scrollTop();
            $(window).unbind('scroll');

        })
})

</script>

<div>

<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
<button id="locker">lock</button>
<button id="unlocker">unlock</button>
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>

</div>
Yeryerevan answered 3/3, 2012 at 0:8 Comment(0)
W
2

Based on this fiddle: http://jsfiddle.net/dh834zgw/1/

the following snippet (using jquery) will disable the window scroll:

 var curScrollTop = $(window).scrollTop();
 $('html').toggleClass('noscroll').css('top', '-' + curScrollTop + 'px');

And in your css:

html.noscroll{
    position: fixed;
    width: 100%;
    top:0;
    left: 0;
    height: 100%;
    overflow-y: scroll !important;
    z-index: 10;
 }

Now when you remove the modal, don't forget to remove the noscroll class on the html tag:

$('html').toggleClass('noscroll');
Whitten answered 1/3, 2016 at 6:44 Comment(1)
Shouldn't the overflow be set to hidden? +1 though as this worked for me (using hidden).Moffit
D
2

Sadly none of the answers above fixed my issues.

In my situation, the web page originally has a scroll bar. Whenever I click the modal, the scroll bar won't disappear and the header will move to right a bit.

Then I tried to add .modal-open{overflow:auto;} (which most people recommended). It indeed fixed the issues: the scroll bar appears after I open the modal. However, another side effect comes out which is that "background below the header will move to the left a bit, together with another long bar behind the modal"

Long bar behind modal

Luckily, after I add {padding-right: 0 !important;}, everything is fixed perfectly. Both the header and body background didn't move and the modal still keeps the scrollbar.

Fixed image

Hope this can help those who are still stuck with this issue. Good luck!

Decapitate answered 18/4, 2017 at 1:54 Comment(0)
B
2

This solution worked for me:

var scrollDistance = 0;
$(document).on("show.bs.modal", ".modal", function () {
    scrollDistance = $(window).scrollTop();
    $("body").css("top", scrollDistance * -1);
});

$(document).on("hidden.bs.modal", ".modal", function () {
    $("body").css("top", "");
    $(window).scrollTop(scrollDistance);
});
.content-area {
  height: 750px;
  background: grey;
  text-align: center;
  padding: 25px;
  font-weight:700;
  font-size: 30px;
}

body.modal-open {
  position: fixed;
  left: 0;
  width: 100%;
}
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.css" rel="stylesheet"/>
<script src="https://code.jquery.com/jquery-3.4.1.slim.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.js"></script>

<div class="content-area">
   Scroll Down To Modal Button<br/>
   <svg xmlns="http://www.w3.org/2000/svg" width="56" height="56" fill="currentColor" class="bi bi-arrow-down" viewBox="0 0 16 16">
      <path fill-rule="evenodd" d="M8 1a.5.5 0 0 1 .5.5v11.793l3.146-3.147a.5.5 0 0 1 .708.708l-4 4a.5.5 0 0 1-.708 0l-4-4a.5.5 0 0 1 .708-.708L7.5 13.293V1.5A.5.5 0 0 1 8 1z"/>
   </svg>
</div>

<center class="my-3">
  <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal">
    Launch demo modal
  </button>
</center>


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


<!-- Modal -->
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
  <div class="modal-dialog" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body">
        <p>Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
        <p>Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor.</p>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
        <button type="button" class="btn btn-primary">Save changes</button>
      </div>
    </div>
  </div>
</div>

Basically when the modal is opened it will add a negative top for the body to maintain the window scroll position before opening the modal. When closing the modal the window scroll is kept using the same value applied for the top when opening. This approach prevents body scroll.

Here is a working fiddle

Bluff answered 3/12, 2021 at 16:26 Comment(0)
C
1

This worked for me:

$("#mymodal").mouseenter(function(){
   $("body").css("overflow", "hidden"); 
}).mouseleave(function(){
   $("body").css("overflow", "visible");
});
Consumer answered 23/4, 2013 at 19:28 Comment(1)
Of all the above answers, this is the first and only one that worked for me.Expunction
L
1

Most of the pieces are here, but I don't see any answer that puts it all together.

The problem is threefold.

(1) prevent the scroll of the underlying page

$('body').css('overflow', 'hidden')

(2) and remove the scroll bar

var handler = function (e) { e.preventDefault() }
$('.modal').bind('mousewheel touchmove', handler)

(3) clean up when the modal is dismissed

$('.modal').unbind('mousewheel touchmove', handler)
$('body').css('overflow', '')

If the modal is not full-screen then apply the .modal bindings to a full screen overlay.

Lothario answered 18/10, 2013 at 11:7 Comment(3)
This also prevents scrolling within the modal.Esme
Same problem. Did you find a solution? @EsmeRubella
@Vay Sort of. Check this out: blog.christoffer.me/…Esme
V
1

My solution for Bootstrap 3:

.modal {
  overflow-y: hidden;
}
body.modal-open {
  margin-right: 0;
}

because for me the only overflow: hidden on the body.modal-open class did not prevent the page from shifting to the left due to the original margin-right: 15px.

Vivi answered 26/2, 2014 at 19:33 Comment(0)
H
1

I just done it this way ...

$('body').css('overflow', 'hidden');

But when the scroller dissapeared it moved everything right 20px, so i added

$('body').css('margin-right', '20px');

straight after it.

Works for me.

Heap answered 22/4, 2015 at 20:9 Comment(1)
This only works if the modal is smaller than the screen size.Damned
H
1

If modal are 100% height/width "mouseenter/leave" will work to easily enable/disable scrolling. This really worked out for me:

var currentScroll=0;
function lockscroll(){
    $(window).scrollTop(currentScroll);
} 
$("#myModal").mouseenter(function(){
    currentScroll=$(window).scrollTop();
    $(window).bind('scroll',lockscroll); 
}).mouseleave(function(){
    currentScroll=$(window).scrollTop();
    $(window).unbind('scroll',lockscroll); 
});
Hyperkinesia answered 7/2, 2016 at 23:24 Comment(0)
P
1

I had a sidebar that was generated by checkbox hack. But the main idea is to save the document scrollTop and not to change it during scrolling the window.

I just didn't like the page jumping when body becomes 'overflow: hidden'.

window.addEventListener('load', function() {
    let prevScrollTop = 0;
    let isSidebarVisible = false;

    document.getElementById('f-overlay-chkbx').addEventListener('change', (event) => {
        
        prevScrollTop = window.pageYOffset || document.documentElement.scrollTop;
        isSidebarVisible = event.target.checked;

        window.addEventListener('scroll', (event) => {
            if (isSidebarVisible) {
                window.scrollTo(0, prevScrollTop);
            }
        });
    })

});
Pretend answered 21/12, 2016 at 13:5 Comment(0)
N
1

Since for me this problem presented mainly on iOS, I provide the code to fix that only on iOS:

  if(!!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform)) {
    var $modalRep    = $('#modal-id'),
        startScrollY = null, 
        moveDiv;  

    $modalRep.on('touchmove', function(ev) {
      ev.preventDefault();
      moveDiv = startScrollY - ev.touches[0].clientY;
      startScrollY = ev.touches[0].clientY;
      var el = $(ev.target).parents('#div-that-scrolls');
      // #div-that-scrolls is a child of #modal-id
      el.scrollTop(el.scrollTop() + moveDiv);
    });

    $modalRep.on('touchstart', function(ev) {
      startScrollY = ev.touches[0].clientY;
    });
  }
Ninetta answered 10/6, 2019 at 19:50 Comment(0)
T
1

None of the above answers worked perfectly for me. So I found another way which works well.

Just add a scroll.(namespace) listener and set scrollTop of the document to the latest of it's value...

and also remove the listener in your close script.

// in case of bootstrap modal example:
$('#myModal').on('shown.bs.modal', function () {
  
  var documentScrollTop = $(document).scrollTop();
  $(document).on('scroll.noScroll', function() {
     $(document).scrollTop(documentScrollTop);
     return false;
  });

}).on('hidden.bs.modal', function() {

  $(document).off('scroll.noScroll');

});

update

seems, this does not work well on chrome. any suggestion to fix it ?

Tense answered 26/11, 2019 at 19:58 Comment(1)
This is a better variation of what I came up with. But alas, I had the problems with Chrome as well. It is is still problematic due to strange and unpredictable side effectsSplenetic
L
1

This works

body.modal-open {
   overflow: hidden !important;
}
Linguistic answered 15/11, 2020 at 10:23 Comment(0)
S
1

I found this to be the best solution:

   export const removeBodyScrollingWhenModalOpen = (modalOpen: boolean) => {
        console.log('modalOpen: ', modalOpen);
        const body = document.body;
        if (modalOpen && isMobile()) {
            const scrollY = document.documentElement.style.getPropertyValue('--scroll-y');
            body.style.position = 'fixed';
            body.style.top = `-${scrollY}`;
        } else if (!modalOpen && isMobile()) {
            const scrollY = body.style.top;
            body.style.position = '';
            body.style.top = '';
            window.scrollTo(0, parseInt(scrollY || '0') * -1);
        } else if (modalOpen) {
            document.body.style.overflowY = 'hidden';
        } else {
            document.body.style.overflowY = 'visible';
        }
    };
Seraphim answered 31/1, 2023 at 16:41 Comment(2)
I added this to an useEffect in my modal component, I don't know if is the best approach, but it's working.Panoply
@AroldoGoulart I spent like a whole day @ work coming up with different solutions. This seems to work best. Thanks for the upvote!Seraphim
A
0

For those wondering how to get the scroll event for the bootstrap 3 modal:

$(".modal").scroll(function() {
    console.log("scrolling!);
});
Assurbanipal answered 5/6, 2014 at 15:25 Comment(0)
J
0

This is the best solution for me:

Css:

.modal {
     overflow-y: auto !important;
}

And Js:

modalShown = function () {
    $('body').css('overflow', 'hidden');
},

modalHidden = function () {
    $('body').css('overflow', 'auto');
}
Johanna answered 26/9, 2014 at 14:51 Comment(0)
O
0

I am using this vanilla js function to add "modal-open" class to the body. (Based on smhmic's answer)

function freezeScroll(show, new_width)
{
    var innerWidth = window.innerWidth,
        clientWidth = document.documentElement.clientWidth,
        new_margin = ((show) ? (new_width + innerWidth - clientWidth) : new_width) + "px";

    document.body.style.marginRight = new_margin;
    document.body.className = (show) ? "modal-open" : "";
};
Obsequent answered 23/10, 2014 at 17:17 Comment(0)
S
0

Hiding the overflow and fixing the position does the trick however with my fluid design it would fix it past the browsers width, so a width:100% fixed that.

body.modal-open{overflow:hidden;position:fixed;width:100%}
Shelashelagh answered 15/11, 2014 at 22:24 Comment(0)
G
0

Try this code:

$('.entry_details').dialog({
    width:800,
    height:500,
    draggable: true,
    title: entry.short_description,
    closeText: "Zamknij",
    open: function(){
        //    blokowanie scrolla dla body
        var body_scroll = $(window).scrollTop();
        $(window).on('scroll', function(){
            $(document).scrollTop(body_scroll);
        });
    },
    close: function(){
        $(window).off('scroll');
    }
}); 
Gainsay answered 18/12, 2014 at 12:36 Comment(0)
A
0

You should add overflow: hidden in HTML for a better cross-platform performance.

I would use

html.no-scroll {
    overflow: hidden;
}
Alcaic answered 7/8, 2015 at 13:51 Comment(0)
A
0
   $('.modal').on('shown.bs.modal', function (e) {
      $('body').css('overflow-y', 'hidden');
   });
   $('.modal').on('hidden.bs.modal', function (e) {
      $('body').css('overflow-y', '');
   });
Awad answered 21/8, 2015 at 13:46 Comment(1)
Please edit with more information. Code-only and "try this" answers are discouraged, because they contain no searchable content, and don't explain why someone should "try this". We make an effort here to be a resource for knowledge.Staford
M
0

A small note for those in SharePoint 2013. The body already has overflow: hidden. What you are looking for is to set overflow: hidden on div element with id s4-workspace, e.g.

var body = document.getElementById('s4-workspace');
body.className = body.className+" modal-open";
Massimiliano answered 26/9, 2016 at 6:59 Comment(0)
C
0

worked for me

$('#myModal').on({'mousewheel': function(e) 
    {
    if (e.target.id == 'el') return;
    e.preventDefault();
    e.stopPropagation();
   }
});
Chordate answered 11/4, 2017 at 9:21 Comment(0)
C
0

The above occurs when you use a modal inside another modal. When I open a modal inside another modal, the closing of the latter removes the class modal-open from the body. The fix of the issue depends on how you close the latter modal.

If you close the modal with html like,

<button type="button" class="btn" data-dismiss="modal">Close</button>

Then you have to add a listener like this,

$(modalSelector).on("hidden.bs.modal", function (event) {
    event.stopPropagation();
    $("body").addClass("modal-open");
    return false;
});

If you close the modal using javascript like,

$(modalSelector).modal("hide");

Then you have to run the command some time after like this,

setInterval(function(){$("body").addClass("modal-open");}, 300);
Complexion answered 26/8, 2017 at 14:44 Comment(0)
L
0

Why not to do that as Bulma does? When modal is-active then add to html their class .is-clipped which is overflow: hidden!important; And thats it.

Edit: Okey, Bulma has this bug, so you must add also other things like

html.is-modal-active {
  overflow: hidden !important;
  position: fixed;
  width: 100%; 
}
Laden answered 19/10, 2018 at 6:13 Comment(0)
P
0

Here's my vanilla JS solution based on @jpap jquery:

let bodyElement = document.getElementsByTagName('body')[0];

// lock body scroll
    let width = bodyElement.scrollWidth;
    bodyElement.classList.add('overflow-hidden');
    bodyElement.style.width = width + 'px';

// unlock body scroll
    bodyElement.classList.remove('overflow-hidden');
    bodyElement.style.width = 'auto';
Peyter answered 24/1, 2021 at 3:16 Comment(0)
C
0

Here's what I do in React to fix this issue:

useEffect(() => {
  if (isShown) {
    const width = document.body.clientWidth;
    document.body.style.overflow = "hidden";
    document.body.style.width = `${width}px`;
  } else {
    document.body.style.overflow = "visible";
    document.body.style.width = `auto`;
  }

  return () => {
    document.body.style.overflow = "visible";
    document.body.style.width = `auto`;
  };
}, [isShown]);
Confinement answered 31/8, 2021 at 21:50 Comment(0)
R
0

I read most answers with a focus on React.

Best solution for my React functional component was to use solution provided originally by @arcticmatt

I included some improvements that were mentioned in other answers in the code example bellow (pay attention to the useEffect definition):

import {useEffect, useRef} from "react";

export default function PopoverMenu({className, handleClose, children}) {
  const selfRef = useRef(undefined);

  useEffect(() => {
    const isPopoverOpenned = selfRef.current?.style.display !== "none";
    const focusedElement = document?.activeElement;
    const scrollPosition = {x: window.scrollX, y: window.scrollY};
    if (isPopoverOpenned) {
      preventDocBodyScrolling();
    } else {
      restoreDocBodyScrolling();
    }

    function preventDocBodyScrolling() {
      const width = document.body.clientWidth;
      const hasVerticalScrollBar = (window.innerWidth > document.documentElement.clientWidth);
      document.body.style.overflowX = "hidden";
      document.body.style.overflowY = hasVerticalScrollBar ? "scroll" : "";
      document.body.style.width = `${width}px`;
      document.body.style.position = "fixed";

    }

    function restoreDocBodyScrolling() {
      document.body.style.overflowX = "";
      document.body.style.overflowY = "";
      document.body.style.width = "";
      document.body.style.position = "";
      focusedElement?.focus();
      window.scrollTo(scrollPosition.x, scrollPosition.y);
    }


    return () => {
      restoreDocBodyScrolling(); // cleanup on unmount
    };
  }, []);

  return (
    <>
      <div
        className="backdrop"
        onClick={() => handleClose && handleClose()}
      />
      <div
        className={`pop-over-menu${className ? (` ${className}`) : ""}`}
        ref={selfRef}
      >
        <button
          className="pop-over-menu--close-button" type="button"
          onClick={() => handleClose && handleClose()}
        >
          X
        </button>
        {children}
      </div>
    </>
  );
}

Rumble answered 1/9, 2021 at 15:18 Comment(0)
B
0

The most famous answer is simple i.e.

body{
   height: 100%;
   overflow-y: hidden;
}

but what will be the solution if you want to open a modal in a child/grand-child and stop the scroll? Well the longer solution will be to use props or store in Angular/React and change the height and overflow property of body tag.

Another solution can just be by getting the body from the child/grand-child component and change its height and overflow accordingly to stop scrolling. In my case I just did this

if(isModalExpanded){
    document.body.style.overflow = "hidden";
    document.body.style.height = "100%";
}
else{
    document.body.style.overflow = "auto";
    document.body.style.height = "auto";
}
Bioplasm answered 23/8, 2022 at 10:26 Comment(0)
A
0

CSS Hack

use CSS :has()

If this class is available in the DOM, overflow will get hidden for Body

body:has(.show_popup) {
   overflow: hidden !important;
}

MDN : https://developer.mozilla.org/en-US/docs/Web/CSS/:has

Amidst answered 6/3 at 12:5 Comment(0)
L
0

I realize that many similar answers have been given, but after struggling with this I wanted to add mine in case it helps anyone.

I did not like how the basic overflow-hidden removes the scrollbar and makes everything jump. So I used the method which extends the body with margin to fill that space and thereby avoid the jarring experience.

I think this works for me because I'm using a dialog element with a darkened overlay and some background blur. It makes the margin, where the scrollbar used to be, hardly noticeable.

// variable to simply avoid unnecessary selector calls
const bodyEl = document.querySelector('body');

// in the function that opens your dialog
let scrollBarWidth = window.innerWidth - bodyEl.clientWidth;
bodyEl.style.overflow = 'hidden';
bodyEl.style.marginRight = videoDialogScrollBarWidth +'px';

// when you close your dialog
bodyEl.style.overflow = '';
bodyEl.style.marginRight = '';
Luralurch answered 11/4 at 15:4 Comment(0)
P
-1

This issue is fixed, Solution: Just open your bootstap.css and change as below

body.modal-open,
.modal-open .navbar-fixed-top,
.modal-open .navbar-fixed-bottom {
  margin-right: 15px;
}

to

 body.modal-open,
.modal-open .navbar-fixed-top,
.modal-open .navbar-fixed-bottom {
  /*margin-right: 15px;*/
}

Please view the below youtube video only less than 3min your issue will fix... https://www.youtube.com/watch?v=kX7wPNMob_E

Porpoise answered 20/6, 2015 at 12:29 Comment(2)
Any one try with this...?Porpoise
I think you misunderstood the whole question. It's not about positioning the scrollbar, it's about preventing to be able to scroll in the background with your mousewheel on pc or finger on mobile devices.Rubella
A
-1

This is a solution in TypeScript that allows easy extension of events to be prevented and deals with mobile devices as well. This also will not cause a horizontal jump when disabling the vertical scrollbar. Just use the exported functions at the bottom on e.g. a onclick handler on any HTML element. If not running in a NodeJS environment, simply delete the process.client checks.

/**
 * Handles scroll events for modal content
 */

const events = [
  'DOMMouseScroll',
  'mousewheel',
  'wheel',
  'touchmove',
  'keydown',
  'mousedown',
  'scroll',
];

const preventDefault = (e: Event) => {
  e.preventDefault();
  e.stopPropagation();
  return false;
};

let currentY: number;

const disableScroll = () => {
  currentY = window.scrollY;

  events.forEach((event) => {
    window.addEventListener(event, preventDefault, {
      passive: false,
    });
  });

  const bodyStyle = document.body.style;
  bodyStyle.setProperty('touch-action', 'none');
  bodyStyle.setProperty('position', 'fixed');
  bodyStyle.setProperty('overflow-y', 'scroll');
  bodyStyle.setProperty('width', '100%');
  bodyStyle.setProperty('top', `-${currentY}px`);
};

const enableScroll = () => {
  events.forEach((event) => {
    window.removeEventListener(event, preventDefault);
  });

  const bodyStyle = document.body.style;
  bodyStyle.removeProperty('touch-action');
  bodyStyle.removeProperty('position');
  bodyStyle.removeProperty('overflow-y');
  bodyStyle.removeProperty('width');
  bodyStyle.removeProperty('top');
  window.scroll(0, currentY);
};

/**
 * Makes a component (e.g. popup) modal
 */
export const makeModal = () => {
  if (process.client) {
    disableScroll();
  }
};

/**
 * Makes a component (e.g. popup) non-modal
 */
export const makeNonModal = () => {
  if (process.client) {
    enableScroll();
  }
};

Accordance answered 30/12, 2022 at 11:39 Comment(0)
H
-3

I found a working solution after doing some 8-10 hours research on StackOverflow itself.

The breakthrough

$('.modal').is(':visible');

So I have built a function to check if any modal is open which will periodically add class *modal-open** to the

 setInterval(function()
     {
         if($('.modal').is(':visible')===true)
         {
             $("body").addClass("modal-open");
         }
         else
         {
             $("body").removeClass("modal-open");
         }

     },200);

The reason to use $(".modal") here is because all modals (in Bootstrap) use class modal (fade/show is as per the state)

So my modals are now running perfectly without the body getting scrolled.

This is a bug/unheard issue in GitHub as well but nobody's bothered.

Hl answered 19/11, 2019 at 14:14 Comment(2)
I'm sure this works, and it's probably even performant if it's the only thing on the page, but as your app grows adding more intervals like this will add up to the slow death of your app's performance.Reciprocate
Rather than polling every 200ms, it would be better to assign an event handler on modal open. E.g. something like $("body").on("show.bs.modal", ".modal", function() { $("body").addClass("modal-open"); });Deltadeltaic
J
-6

HTML:

<body onscroll="stop_scroll()">

javascript:

function stop_scroll(){
    scroll(0,0) ;
}

If you set a flag (bool) inside stop_scroll(), you can decide when to engage it (if you want it only temporarely).

This will reset scrolling every time some element overflows the body boundaries and the windows tends to scroll (this is totally independent of scrollbars; overflow : hidden has nothing to do with it).

Jacobite answered 25/8, 2013 at 12:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.