How to prevent background scrolling when Bootstrap 3 modal open on mobile browsers?

See here: https://github.com/twbs/bootstrap/issues/7501

So try:

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

V3.0.0. should have fixed this issue. Do you use the latest version? If so post an issue on https://github.com/twbs/bootstrap/

Try this,

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

I tried the accepted answer which prevented the body from scrolling but had the issue of scrolling to the top. This should solve both issues.

As a side note, it appears overflow:hidden doesn't work on body for iOS Safari only as iOS Chrome works fine.

var scrollPos = 0;

$('.modal')
    .on('show.bs.modal', function (){
        scrollPos = $('body').scrollTop();
        $('body').css({
            overflow: 'hidden',
            position: 'fixed',
            top : -scrollPos
        });
    })
    .on('hide.bs.modal', function (){
        $('body').css({
            overflow: '',
            position: '',
            top: ''
        }).scrollTop(scrollPos);
    });
$('.modal') 
.on('shown', function(){ 
  console.log('show'); 
  $('body').css({overflow: 'hidden'}); 
}) 
.on('hidden', function(){ 
  $('body').css({overflow: ''}); 
}); 

use this one

Martin

No scripts needed.

BS 3 sets a .modal-open class on body that you can use to set the position and overflow values (made with LESS).

body {
    font-family:'Open Sans';
    margin:0;

    &.modal-open {
        position:fixed;
        overflow:hidden;

        .modal { 
            overflow: scroll;

            @media only screen and (min-resolution:150dpi) and (max-width: @screen-sm),
            only screen and (-webkit-min-device-pixel-ratio:1.5) {
                overflow: scroll; 
                -webkit-overflow-scrolling: touch; 
            }
        }
    }   
}

If you use jQuery you can do this with scrollTop

  1. Save the body's vertical scroll position;
  2. Disable scroll on the body;
  3. Show modal;
  4. Close modal;
  5. Reenable scroll on body;
  6. Set saved scroll position.

#modal {
    bottom: 0;
    position: fixed;
    overflow-y: scroll;
    overflow-x: hidden;
    top: 0;
    width: 100%;
}

$('.open-modal').click(function (e) {
    e.preventDefault();
    $('#modal').toggle();
    scrollTo = $('body').scrollTop();
    $('body').css("position", "fixed");
});

$('.close-modal').click(function (e) {
    e.preventDefault();
    $('#modal').toggle();
    $('body').css("position", "static");
    $('body').animate({scrollTop: scrollTo}, 0);
});

The chosen solution works, however they also snap the background to the top scrolling position. I extended the code above to fix that 'jump'.

//Set 2 global variables
var scrollTopPosition = 0;
var lastKnownScrollTopPosition = 0;

//when the document loads
$(document).ready(function(){

  //this only runs on the right platform -- this step is not necessary, it should work on all platforms
  if( navigator.userAgent.match(/iPhone|iPad|iPod/i) ) {

    //There is some css below that applies here
    $('body').addClass('platform-ios');

    //As you scroll, record the scrolltop position in global variable
    $(window).scroll(function () {
      scrollTopPosition = $(document).scrollTop();
    });

    //when the modal displays, set the top of the (now fixed position) body to force it to the stay in the same place
    $('.modal').on('show.bs.modal', function () {

      //scroll position is position, but top is negative
      $('body').css('top', (scrollTopPosition * -1));

      //save this number for later
      lastKnownScrollTopPosition = scrollTopPosition;
    });

    //on modal hide
    $('.modal').on('hidden.bs.modal', function () {

      //force scroll the body back down to the right spot (you cannot just use scrollTopPosition, because it gets set to zero when the position of the body is changed by bootstrap
      $('body').scrollTop(lastKnownScrollTopPosition);
    });
  }
});

The css is pretty simple:

// You probably already have this, but just in case you don't
body.modal-open {
  overflow: hidden;
  width: 100%;
  height: 100%;
}
//only on this platform does it need to be fixed as well
body.platform-ios.modal-open {
  position: fixed;
}

Had an issue with this as well, iPhone + Safari where needed to add:

position: fixed;

As mentioned elsewhere, this created a scroll-to-top issue. Fix that worked for me was to capture the position to top upon modal open, and then animate to that position on modal close

upon modal open:

scrollTo = $('body').scrollTop();
$('body').css("position", "fixed");

upon modal close

$('body').css("position", "static");
$('body').animate({scrollTop: scrollTo}, 0);
Micheal

I thought you might forget to add attribute data-toggle="modal" to the link or button that triggers the modal popup event. Firstly, I also got the same problem but, after adding the attribute above it works well for me.

The above answers did not help so what I did was:

.modal {
  -webkit-overflow-scrolling: touch; 
}

My particular problem was when I increased the modal size after loading.

It's a known iOS issue, see here. Since it does not break anything else the above solution was enough for my needs.

This might be a bit like beating a dead horse here.. but, my currently implemented solution on DIY modals via vanilla JS:

On modal show:

if (document.body.style.position !== 'fixed') {
    document.body.style.top = -window.scrollY + 'px';
    document.body.style.position = 'fixed';
}

On modal hide:

document.body.style.position = '';
window.scrollTo(0, -parseInt(document.body.style.top, 10));
document.body.style.top = '';
madzohan

As additional to @Karthick Kumar answer from bootstrap docs

show is triggered at the start of an event

shown is triggered on the completion of an action

... so it should be:

$('.modal')
    .on('show.bs.modal', function (){
            $('body').css('overflow', 'hidden');
        })
    .on('hide.bs.modal', function (){
            // Also if you are using multiple modals (cascade) - additional check
            if ($('.modal.in').length == 1) {
                $('body').css('overflow', 'auto');
            }
        });

Hey guys so i think i found a fix. This is working for me on iphone and android at the moment. Its a mash up of hours upon hours of searching, reading and testing. So if you see parts of your code in here credit goes to you lol.

@media only screen and (max-device-width:768px){

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; 
}

body {  
overflow-x: hidden;
overflow-y: scroll !important;
}

The reason the media specific is on there is on a desktop i was having issues with when the modal would open all content on the page would shift from centered to left. Looked like crap. So this targets up to tablet size devices where you would need to scroll. There is still a slight shift on mobile and tablet but its really not much. Let me know if this works for you guys. Hopefully this puts the nail in the coffin

Truchainz

I've found a simple javascript/jquery solution which utilizes the bootstrap modal events.

My solution also fixes the position:fixed problem where it scrolls the background page all the way back to the top instead of staying in place when modal window is opened/closed.

See details here

I know this has been answered but none of these solutions worked for me. I needed to take a different approach. I am using PhoneGap and am compiling my code natively so I had to move the background to the body. I hope this helps someone else. Or if there a cleaner way to do this please feel free to comment...

$(document).on('shown.bs.modal', '.modal', function (e) {

    $("#" + e.target.id).find(".modal-backdrop").css("z-index", $("#" + e.target.id).css("z-index")).insertBefore("#" + e.target.id);

});

Using position:fixed has the side effect of scrolling the body to the top.

If you do not your body to scroll to the top, DO NOTE use position:fixed. Just disable touchmove on the body if the modal is open. Note: The modal itself is still able to scroll on touch (if larger than the screen).

CSS:

body.modal-open {
    overflow: hidden;
    width: 100%;
    /* NO position:fixed here*/
}

JS:

$('.modal').on('show.bs.modal', function (ev) { // prevent body from scrolling when modal opens
    $('body').bind('touchmove', function(e){
        if (!$(e.target).parents().hasClass( '.modal' )){ //only prevent touch move if it is not the modal
            e.preventDefault()
        }
    })
})
$('.modal').on('hide.bs.modal', function (e) { //unbind the touchmove restrictions from body when modal closes
    $('body').unbind('touchmove');
})

EDIT: Note, for very small modals, you might have to add the following line to your CSS:

.modal-dialog{
    height: 100%;
}

Courtesy of JDiApice who synthesized and extended the work of other contributors at iOS 8.x modal scroll issue #14839:

@media only screen and (max-device-width:768px) {

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;
}

body {
    overflow-x: hidden;
    overflow-y: scroll !important;
}

/* The reason the media specific is on there is 
   on a desktop i was having issues with when the modal would open 
   all content on the page would shift from centered to left. 
   Looked like crap. So this targets up to tablet size devices 
   where you would need to scroll. There is still a slight shift 
   on mobile and tablet but its really not much. */

Unlike the other solutions we tried, it does not scroll the background to the top after the popup modal closes.

I open a modal after a modal and fact the error on modal scrolling, and this css solved my problem:

.modal {
    overflow-y: auto;
    padding-right: 15px;
}

Prevents the background from scrolling without any fuss.

Also prevents page jumping around from missing scrollbars

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

My solution...

Ver en jsfiddle

//Fix modal mobile Boostrap 3
function Show(id){
    //Fix CSS
    $(".modal-footer").css({"padding":"19px 20px 20px","margin-top":"15px","text-align":"right","border-top":"1px solid #e5e5e5"});
    $(".modal-body").css("overflow-y","auto");
    //Fix .modal-body height
    $('#'+id).on('shown.bs.modal',function(){
        $("#"+id+">.modal-dialog>.modal-content>.modal-body").css("height","auto");
        h1=$("#"+id+">.modal-dialog").height();
        h2=$(window).height();
        h3=$("#"+id+">.modal-dialog>.modal-content>.modal-body").height();
        h4=h2-(h1-h3);      
        if($(window).width()>=768){
            if(h1>h2){
                $("#"+id+">.modal-dialog>.modal-content>.modal-body").height(h4);
            }
            $("#"+id+">.modal-dialog").css("margin","30px auto");
            $("#"+id+">.modal-dialog>.modal-content").css("border","1px solid rgba(0,0,0,0.2)");
            $("#"+id+">.modal-dialog>.modal-content").css("border-radius",6);               
            if($("#"+id+">.modal-dialog").height()+30>h2){
                $("#"+id+">.modal-dialog").css("margin-top","0px");
                $("#"+id+">.modal-dialog").css("margin-bottom","0px");
            }
        }
        else{
            //Fix full-screen in mobiles
            $("#"+id+">.modal-dialog>.modal-content>.modal-body").height(h4);
            $("#"+id+">.modal-dialog").css("margin",0);
            $("#"+id+">.modal-dialog>.modal-content").css("border",0);
            $("#"+id+">.modal-dialog>.modal-content").css("border-radius",0);   
        }
        //Aply changes on screen resize (example: mobile orientation)
        window.onresize=function(){
            $("#"+id+">.modal-dialog>.modal-content>.modal-body").css("height","auto");
            h1=$("#"+id+">.modal-dialog").height();
            h2=$(window).height();
            h3=$("#"+id+">.modal-dialog>.modal-content>.modal-body").height();
            h4=h2-(h1-h3);
            if($(window).width()>=768){
                if(h1>h2){
                    $("#"+id+">.modal-dialog>.modal-content>.modal-body").height(h4);
                }
                $("#"+id+">.modal-dialog").css("margin","30px auto");
                $("#"+id+">.modal-dialog>.modal-content").css("border","1px solid rgba(0,0,0,0.2)");
                $("#"+id+">.modal-dialog>.modal-content").css("border-radius",6);               
                if($("#"+id+">.modal-dialog").height()+30>h2){
                    $("#"+id+">.modal-dialog").css("margin-top","0px");
                    $("#"+id+">.modal-dialog").css("margin-bottom","0px");
                }
            }
            else{
                //Fix full-screen in mobiles
                $("#"+id+">.modal-dialog>.modal-content>.modal-body").height(h4);
                $("#"+id+">.modal-dialog").css("margin",0);
                $("#"+id+">.modal-dialog>.modal-content").css("border",0);
                $("#"+id+">.modal-dialog>.modal-content").css("border-radius",0);   
            }
        };
    });  
    //Free event listener
    $('#'+id).on('hide.bs.modal',function(){
        window.onresize=function(){};
    });  
    //Mobile haven't scrollbar, so this is touch event scrollbar implementation
    var y1=0;
    var y2=0;
    var div=$("#"+id+">.modal-dialog>.modal-content>.modal-body")[0];
    div.addEventListener("touchstart",function(event){
        y1=event.touches[0].clientY;
    });
    div.addEventListener("touchmove",function(event){
        event.preventDefault();
        y2=event.touches[0].clientY;
        var limite=div.scrollHeight-div.clientHeight;
        var diff=div.scrollTop+y1-y2;
        if(diff<0)diff=0;
        if(diff>limite)diff=limite;
        div.scrollTop=diff;
        y1=y2;
    });
    //Fix position modal, scroll to top.    
    $('html, body').scrollTop(0);
    //Show
    $("#"+id).modal('show');
}