Impedir que o CORPO role enquanto um modal é aberto

346

Quero que meu corpo pare de rolar ao usar a roda do mouse enquanto o Modal (em http://twitter.github.com/bootstrap ) no meu site é aberto.

Eu tentei chamar o pedaço de javascript abaixo quando o modal é aberto, mas sem sucesso

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

E

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

No entanto, nosso site eliminou o suporte ao IE6, IE7 +, mas precisa ser compatível.

xorinzor
fonte

Respostas:

468

O Bootstrap modaladiciona automaticamente a classe modal-openao corpo quando uma caixa de diálogo modal é exibida e a remove quando a caixa de diálogo está oculta. Portanto, você pode adicionar o seguinte ao seu CSS:

body.modal-open {
    overflow: hidden;
}

Você pode argumentar que o código acima pertence à base de código CSS do Bootstrap, mas essa é uma solução fácil para adicioná-lo ao seu site.

Atualização 8 de fevereiro de 2013
Agora, isso parou de funcionar no Twitter Bootstrap v. 2.3.0 - eles não adicionam mais a modal-openclasse ao corpo.

Uma solução alternativa seria adicionar a classe ao corpo quando o modal estiver prestes a ser exibido e removê-lo quando o modal estiver fechado:

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

Atualização 11 de março de 2013 Parece que a modal-openclasse retornará no Bootstrap 3.0, explicitamente com o objetivo de impedir a rolagem:

Reintroduz a abertura .modal no corpo (para que possamos nuke o pergaminho lá)

Veja isto: https://github.com/twitter/bootstrap/pull/6342 - veja a seção Modal .

MartinHN
fonte
2
isso não funciona mais no bootstrap 2.2.2. Espero que o .modal-open volte no futuro ... github.com/twitter/bootstrap/issues/5719
ppetrid
2
@ppetrid Não espero que retorne. Com base nesta redação: github.com/twitter/bootstrap/wiki/Upcoming-3.0-changes (veja a parte inferior). Citar: "Chega de rolagem modal interna. Em vez disso, os modais crescerão para abrigar seu conteúdo e a rolagem da página para abrigar o modal".
MartinHN
2
@Bagata Cool - modal-openretornará no Bootstrap 3; portanto, quando for iniciado, será seguro remover o código acima.
MartinHN
2
É por isso que se deve continuar rolando para baixo, se a resposta escolhida não o satisfizer, há chances de que você encontre pedras como essas. não esperava uma resposta com mais de 97 votos enterrada tão profundamente em outros comentários menos curtidos.
Mohd Abdul Mujib
2
o problema é que, para impedir que o documento role para o topo quando um modal é aberto, é necessário adicionar body.modal-open {overflow: visible}. Sua solução funciona no momento, com a desvantagem de que o documento rola para o topo assim que o modal é aberto
patrick
120

A resposta aceita não funciona no celular (iOS 7 com Safari 7, pelo menos) e não quero o MOAR JavaScript em execução no meu site quando o CSS funcionar.

Este CSS impedirá que a página de segundo plano role sob o modal:

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

No entanto, ele também tem um leve efeito colateral de rolar para o topo. position:absoluteresolve isso, mas reintroduz a capacidade de rolar no celular.

Se você conhece a sua viewport ( meu plugin para adicionar a viewport à<body> ), basta adicionar uma alternância de css para a 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; 
}

Também adiciono isso para impedir que a página subjacente salte para a esquerda / direita ao mostrar / ocultar modais.

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

esta resposta é um x-post.

Brad
fonte
7
Obrigado! Os celulares (pelo menos, navegador nativo iOS e Android) estavam me dando dor de cabeça e não funcionavam sem position: fixedo corpo.
Raul Rene
Obrigado por incluir também o código para impedir que a página salte para a esquerda / direita ao exibir / ocultar modais. Isso foi extremamente útil e verifiquei que ele corrigia um problema que eu estava tendo no Safari em um iPhone 5c executando o iOS 9.2.1.
Elijah Lofgren
6
Para corrigir a rolagem para o topo, você pode gravar a posição antes de adicionar a classe e, depois que a classe for removida, faça window.scroll na posição gravada. Foi assim que eu o corrigi : pastebin.com/Vpsz07zd .
Ferramenta
Embora eu ache que isso atenda apenas às necessidades básicas, foi uma solução útil. Obrigado.
Steve Benner
32

Simplesmente oculte o excesso de corpo e isso fará com que o corpo não role. Quando você oculta o modal, reverta-o para automático.

Aqui está o código:

$('#adminModal').modal().on('shown', function(){
    $('body').css('overflow', 'hidden');
}).on('hidden', function(){
    $('body').css('overflow', 'auto');
})
Mehmet Fatih Yıldız
fonte
Excelente! Isso me ajudou. Obrigado.
Pfcodes
21

Você pode tentar definir o tamanho do corpo para o tamanho da janela com estouro: oculto quando o modal estiver aberto

charlietfl
fonte
12
isso iria mover a tela toda vez que o modal é aberto, que não é conveniente, eu só preciso desativar a rolagem fundo
xorinzor
não tenho certeza que você pode barras de rolagem são browser relacionado e você não pode fazer muito com eles
charlietfl
você pode definir modal de "fixo" para que não se moverá ao rolar
charlietfl
8
não é sobre parar o modal de rolagem, é sobre parar o corpo no fundo de rolagem
xorinzor
11
é a janela que rola se o corpo o exceder. Por exemplo, as barras de rolagem não fazem parte do documento.
22412 charlietfl #
18

Você precisa ir além da resposta de @ charlietfl e considerar as barras de rolagem; caso contrário, poderá ver um documento refluir.

Abrindo o modal:

  1. Grave a bodylargura
  2. Defina o bodyestouro comohidden
  3. Defina explicitamente a largura do corpo para o que era na etapa 1.

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

Fechando o modal:

  1. Defina o bodyestouro comoauto
  2. Defina a bodylargura comoauto

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

Inspirado em: http://jdsharp.us/jQuery/minute/calculate-scrollbar-width.php

jpap
fonte
Eu estava recebendo uma tela "irregular" por falta de um termo melhor e a chave aqui para mim era seguir as configurações de largura. Muito obrigado.
Hcabnettek
11
@Hcabnettek Sim: "jumpy" == "documento reflow". Que bom que ajudou! :-)
jpap 6/09/13
existe uma solução css para isso? funciona em todos os navegadores e dispositivos móveis?
Ruben
apenas um erro: Deve ser $body.css("overflow", "auto");na última etapa :)
schneikai
Oh cara, que ideia maravilhosa. @ jpap, você me ajudou a resolver o problema de refluxo do documento que estava me incomodando por um longo tempo.
Goran Radulovic
17

Aviso: A opção abaixo não é relevante para o Bootstrap v3.0.x, pois a rolagem nessas versões foi explicitamente confinada ao próprio modal. Se você desativar os eventos de roda, poderá inadvertidamente impedir que alguns usuários visualizem o conteúdo em modais com alturas maiores que a altura da janela de exibição.


Outra opção: eventos de roda

O evento de rolagem não é cancelável. No entanto, é possível cancelar os eventos de roda do mouse e roda . A grande ressalva é que nem todos os navegadores legados os suportam, a Mozilla recentemente adicionou suporte para este último no Gecko 17.0. Não conheço a propagação completa, mas o IE6 + e o Chrome os suportam.

Veja como aproveitá-los:

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

JSFiddle

merv
fonte
Seu JSFiddle não funciona no Firefox 24, Chrome 24 nem no IE9.
AxeEffect
@AxeEffect deve estar funcionando agora. O único problema era que as referências externas à biblioteca Bootstrap haviam sido alteradas. Obrigado pela atenção.
Merv # 30/13
2
Isso não impede que os dispositivos de toque rolem, overflow:hiddené melhor por enquanto.
Webinan
5
todos estão funcionando, mas não para dispositivos móveis. -__- Android 4.1 e Chrome testados
Tower Jimmy
@TowerJimmy você provavelmente precisará cancelar o toque ou arrastar eventos para que (se isso é mesmo possível)
xorinzor
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(); });
});
jparkerweb
fonte
9

Não foi possível fazê-lo funcionar no Chrome apenas alterando o CSS, porque não queria que a página voltasse ao topo. Isso funcionou bem:

$("#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);
});
tetsuo
fonte
11
esta é a única solução que funcionou para mim no bootstrap 3.2.
volx757
11
Teve o salto para o principal problema com sidenavs de material angular. Essa foi a única resposta que parece funcionar em todos os casos (desktop / celular + permitir rolagem no modal / sidenav + deixar a posição de rolagem do corpo fixa sem pular).
precisa saber é o seguinte
Essa também é a única solução que realmente funcionou para mim. tx
Deedz 12/12
funcionou para mim, bootstrap v4
Deano
9

Tente este:

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

funcionou para mim. (suporta IE8)

fatCop
fonte
4
Isso funciona, mas também faz com que você pule para o topo quando você abre um modal ao usá-lo position: fixed.
AlexioVay
8

Para Bootstrap , que você pode tentar isso (trabalhando em Firefox, Chromee Microsoft Edge):

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

Espero que isto ajude...

Murat Yıldız
fonte
7

Adicionar a classe 'is-modal-open' ou modificar o estilo da tag body com javascript é bom e funcionará como deveria. Mas o problema que enfrentaremos é quando o corpo ficar sobrecarregado: oculto, ele irá pular para o topo (scrollTop se tornará 0). Isso se tornará um problema de usabilidade mais tarde.

Como solução para esse problema, em vez de alterar o estouro da tag body: oculto altere-o na tag html

$('#myModal').on('shown.bs.modal', function () {
  $('html').css('overflow','hidden');
}).on('hidden.bs.modal', function() {
  $('html').css('overflow','auto');
});
Jassim Abdul Latheef
fonte
11
Esta é absolutamente a resposta correta, porque a resposta de consenso geral rola a página para o topo, o que é uma experiência horrível para o usuário. Você deve escrever um blog sobre isso. Acabei transformando isso em uma solução global.
Smith
Isso não funcionará no iphone.
BadriNarayanan Sridharan
6

Não tenho certeza sobre esse código, mas vale a pena tentar.

No jQuery:

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

Como eu disse antes, não tenho 100% de certeza, mas tente de qualquer maneira.

Anish Gupta
fonte
11
Isso não impede o organismo de rolagem, enquanto o modal é aberto
xorinzor
@xorinzor você declarou na seção de comentários da resposta de charlietfl que isso funciona. Mas você disse: Isso não impede que o corpo role enquanto o modal é aberto.
Anish Gupta
verdade, mas por enquanto é a única solução que funciona parcialmente e estou bem. a razão pela qual marquei sua resposta como resposta é porque ele foi mais cedo com a resposta.
precisa saber é o seguinte
@xorinzor realmente, eu pensei ter respondido antes dele / dela, pode ser uma falha estranha. é bom embora
Anish Gupta 04/04
4

A melhor solução é a body{overflow:hidden}solução somente de css usada pela maioria dessas respostas. Algumas respostas fornecem uma correção que também evita o "salto" causado pela barra de rolagem que desaparece; no entanto, nenhum era muito elegante. Então, eu escrevi essas duas funções, e elas parecem funcionar muito bem.

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

Confira este jsFiddle para vê-lo em uso.

Stephen M. Harris
fonte
Isso não é tão antigo, mas nada disso está funcionando para mim, nem mesmo o violino que ainda posso rolar pela janela, o que estou perdendo?
desembarcou
Abra e feche o modal algumas vezes, e o DIV verde principal encolhe e encolhe!
Webinan
@ Webinan Não estou vendo isso no Chrome no Win7. Qual é o tamanho do seu navegador e viewport?
Stephen M. Harris
11
@smhmic após cerca de 20 vezes de abertura e fechamento, a largura do div verde torna-se igual à open modallargura dos botões. Chrome on 8
Webinan 5/08/2014
Semelhante à resposta do @ jpap ( stackoverflow.com/a/16451821/5516499 ), mas com bugs adicionais. :-(
Kivi Shapiro
4

Muitos sugerem "overflow: hidden" no corpo, o que não funcionará (pelo menos no meu caso), pois fará o site rolar para o topo.

Esta é a solução que funciona para mim (tanto em telefones celulares quanto em computadores), usando o 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');
    });

Isso fará com que a rolagem do modal funcione e impedirá que o site role ao mesmo tempo.

Danie
fonte
Parece que esta solução também corrige esse bug do iOS: hackernoon.com/…
Gotenks
3

Você pode usar a seguinte lógica, eu testei e funciona (mesmo no 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>
miríade
fonte
2

Não tenho 100% de certeza de que isso funcionará com o Bootstrap, mas vale a pena tentar - ele funcionou com o Remodal.js, que pode ser encontrado no github: http://vodkabears.github.io/remodal/ e faria sentido para os métodos para ser bem parecido.

Para parar a página pulando para o topo e também impedir a troca correta de conteúdo, adicione uma classe ao bodyquando o modal for acionado e defina estas regras de CSS:

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

É position:statico height:autoque combinam para parar o salto do conteúdo para a direita. O overflow-y:hidden;pára a página de ser scrollable atrás do modal.

AdamJB
fonte
Esta solução funciona perfeitamente para mim no Safari 11.1.1, Chrome 67.0.3396.99 e Firefox 60.0.2. Obrigado!
Dom
2

Com base neste violino: http://jsfiddle.net/dh834zgw/1/

o seguinte snippet (usando jquery) desabilitará a rolagem da janela:

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

E no seu css:

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

Agora, quando você remover o modal, não se esqueça de remover a classe noscroll na tag html:

$('html').toggleClass('noscroll');
ling
fonte
Não deveria overflowser definido como hidden? +1, pois isso funcionou para mim (usando hidden).
Mhulse
2

Eu tinha uma barra lateral que foi gerada pelo hack da caixa de seleção. Mas a idéia principal é salvar o documento scrollTop e não alterá-lo durante a rolagem da janela.

Eu simplesmente não gostei da página pulando quando o corpo se torna '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);
            }
        });
    })

});

Lozhachevskaya Anastasiya
fonte
2

Infelizmente, nenhuma das respostas acima corrigiu meus problemas.

Na minha situação, a página da web originalmente possui uma barra de rolagem. Sempre que clico no modal, a barra de rolagem não desaparece e o cabeçalho se move para a direita um pouco.

Então tentei adicionar .modal-open{overflow:auto;}(o que a maioria das pessoas recomendou). Ele realmente corrigiu os problemas: a barra de rolagem aparece depois que eu abro o modal. No entanto, outro efeito colateral é o de que "o fundo abaixo do cabeçalho se moverá para a esquerda um pouco, junto com outra barra longa atrás do modal"

Barra longa atrás de modal

Felizmente, depois de acrescentar {padding-right: 0 !important;}, tudo está corrigido perfeitamente. O cabeçalho e o fundo do corpo não se moveram e o modal ainda mantém a barra de rolagem.

Imagem fixa

Espero que isso ajude aqueles que ainda estão presos a esse problema. Boa sorte!

Bocard Wang
fonte
1

A maioria das peças está aqui, mas não vejo nenhuma resposta que junte tudo.

O problema é triplo.

(1) impedir a rolagem da página subjacente

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

(2) e remova a barra de rolagem

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

(3) limpar quando o modal for demitido

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

Se o modal não for tela cheia, aplique as ligações .modal a uma sobreposição de tela cheia.

evoskuil
fonte
4
Isso também impede a rolagem dentro do modal.
Daniel
Mesmo problema. Você encontrou uma solução? @Daniel
AlexioVay
@Vay Mais ou menos. Verifique isso: blog.christoffer.me/…
Daniel
1

Minha solução para o Bootstrap 3:

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

porque para mim o único overflow: hiddenaluno da body.modal-openturma não impediu que a página se movesse para a esquerda devido ao original margin-right: 15px.

Pierrea
fonte
1

Acabei de fazer desta maneira ...

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

Mas quando o scroller desapareceu, mudou tudo 20px, então eu adicionei

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

logo depois.

Funciona para mim.

Penhasco
fonte
Isso funciona apenas se o modal for menor que o tamanho da tela.
Sergey
1

Se os modais tiverem 100% de altura / largura, "mouseenter / leave" funcionará para ativar / desativar a rolagem com facilidade. Isso realmente funcionou para mim:

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); 
});
Mats
fonte
1

trabalhou para mim

$('#myModal').on({'mousewheel': function(e) 
    {
    if (e.target.id == 'el') return;
    e.preventDefault();
    e.stopPropagation();
   }
});
namal
fonte
1

Por que não fazer isso como Bulma? Quando modal estiver ativo, adicione ao html sua classe .is-clipped que está excedente: oculto! Importante; E é isso.

Edit: Okey, Bulma tem esse bug, então você deve adicionar também outras coisas como

html.is-modal-active {
  overflow: hidden !important;
  position: fixed;
  width: 100%; 
}
nrkz
fonte
1

Como para mim esse problema apresentado principalmente no iOS, forneço o código para corrigir isso apenas no 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;
    });
  }
LowFieldTheory
fonte
1

Nenhuma das respostas acima funcionou perfeitamente para mim. Então eu encontrei outra maneira que funciona bem.

Basta adicionar um scroll.(namespace)ouvinte e conjunto scrollTopdo documentque o mais recente do seu valor ...

e também remova o ouvinte no seu script de fechamento.

// 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');

});

atualizar

parece, isso não funciona bem no chrome. alguma sugestão para corrigi-lo?

Pars
fonte
0

Isso funcionou para mim:

$("#mymodal").mouseenter(function(){
   $("body").css("overflow", "hidden"); 
}).mouseleave(function(){
   $("body").css("overflow", "visible");
});
Gerhard Liebenberg
fonte
0

Para aqueles que querem saber como obter o evento scroll para o modal de inicialização 3:

$(".modal").scroll(function() {
    console.log("scrolling!);
});
Arie
fonte