A rolagem do div filho rola a janela, como faço para parar isso?

94

Eu tenho um div, com uma barra de rolagem, quando chega ao fim, minha página começa a rolar. Existe alguma maneira de interromper esse comportamento?

Jeevan
fonte
2
@NGLN não é uma pergunta duplicada, diferente.
sidewinderguy
1
Possível duplicata de Impedir rolagem do elemento pai quando a posição
TylerH

Respostas:

68

Você pode desativar a rolagem de toda a página fazendo algo assim:

<div onmouseover="document.body.style.overflow='hidden';" onmouseout="document.body.style.overflow='auto';"></div>
Teemu
fonte
33
Ótimo, mas faz a barra de rolagem do navegador desaparecer sempre que você passa o mouse sobre o div.
cstack de
@cstack você está certo, é por isso que a própria resposta do OP (Jeevan) é melhor.
Imran Bughio
2
Muitas rodas de rolagem do navegador desaparecem e reaparecem com base no usuário movendo o mouse agora, portanto, o comentário acima não é mais um problema.
Keith Holliday de
No entanto, essa solução não funciona quando a página está incorporada em um iframe.
Gautam Krishnan
5
não é uma solução. definir o estouro como oculto oculta a barra de rolagem que reformata repentinamente todo o conteúdo do elemento externo. além disso, assume que o elemento externo é o corpo, mas poderia ser outro div de rolagem.
robisrob
43

Encontrei a solução.

http://jsbin.com/itajok

Isso é o que eu precisava.

E este é o código.

http://jsbin.com/itajok/edit#javascript,html

Usa um plug-in jQuery.


Atualização devido ao aviso de suspensão de uso

De jquery-mousewheel :

O antigo comportamento de adicionar três argumentos ( delta, deltaXe deltaY) ao manipulador de eventos agora está obsoleto e será removido em versões posteriores.

Então, event.deltaYagora deve ser usado:

var toolbox = $('#toolbox'),
    height = toolbox.height(),
    scrollHeight = toolbox.get(0).scrollHeight;

toolbox.off("mousewheel").on("mousewheel", function (event) {
  var blockScrolling = this.scrollTop === scrollHeight - height && event.deltaY < 0 || this.scrollTop === 0 && event.deltaY > 0;
  return !blockScrolling;
});

Demo

Jeevan
fonte
6
Seu link usa github para o js e quebra porque o tipo de conteúdo, blá, blá, aqui, este funciona: jsbin.com/itajok/207
Michael J. Calkins
Ambos os jsbins não parecem funcionar no emulador de dispositivo Chrome e no iphone. Alguém sabe se isso ainda funciona?
Dirk Boer
2
Sua "solução" só funciona para eventos de rolagem do mouse, não para as teclas de página para cima / para baixo ou de seta.
Scott
Por que "off" é obrigatório aqui?
Brad Johnson
16

A solução selecionada é uma obra de arte. Achei que valia a pena um plugin ....

$.fn.scrollGuard = function() {
    return this
        .on( 'wheel', function ( e ) {
            var event = e.originalEvent;
            var d = event.wheelDelta || -event.detail;
            this.scrollTop += ( d < 0 ? 1 : -1 ) * 30;
            e.preventDefault();
        });
};    

Isso tem sido um incômodo contínuo para mim e essa solução é tão limpa em comparação com outros hacks que já vi. Curioso para saber mais sobre como funciona e como seria amplamente apoiado, mas um brinde a Jeevan e a quem o criou originalmente. BTW - o editor de respostas stackoverflow precisa disso!

ATUALIZAR

Eu acredito que isso é melhor porque ele não tenta manipular o DOM, apenas evita o borbulhamento condicionalmente ...

$.fn.scrollGuard2 = function() {
  return this
    .on( 'wheel', function ( e ) {
      var $this = $(this);
      if (e.originalEvent.deltaY < 0) {
        /* scrolling up */
        return ($this.scrollTop() > 0);
      } else {
        /* scrolling down */
        return ($this.scrollTop() + $this.innerHeight() < $this[0].scrollHeight);
      }
    })
  ;
};    

Funciona muito bem no cromo e muito mais simples do que outras soluções ... deixe-me saber como fica em outro lugar ...

FIDDLE

Robisrob
fonte
3
adicionar o evento DOMMouseScroll o faz funcionar com Firefox → jsfiddle.net/chodorowicz/egqy7mbz/1
chodorowicz
Estou surpreso que isso não esteja escrito em jquery. Achei que um dos pontos principais era não me importar com o navegador que você está usando.
robisrob
na verdade, parece que ambos estão obsoletos ... wheelevento
robisrob
Isso resulta em uma rolagem visivelmente mais irregular no Chrome no meu Macbook. A solução aceita não tem esse problema.
danvk
decepcionante. existem tantas versões diferentes disso. parece ser um aspecto importante da interface do usuário, e realmente é surpreendente que esse seja o comportamento padrão quando é tão pouco intuitivo. Obviamente, o navegador está equipado para saber que a div de rolagem interna está em seu limite para poder aplicar o evento à janela, então por que não há identificadores disponíveis para consultá-lo?
robisrob
8

Você poderia usar um evento mouseover no div para desativar a barra de rolagem do corpo e, em seguida, um evento mouseout para ativá-lo novamente?

Por exemplo, o HTML

<div onmouseover="disableBodyScroll();" onmouseout="enableBodyScroll();">
    content
</div>

E então o javascript assim:

var body = document.getElementsByTagName('body')[0];
function disableBodyScroll() {
    body.style.overflowY = 'hidden';
}
function enableBodyScroll() {
    body.style.overflowY = 'auto';
}
joe92
fonte
12
Você também pode usar document.body.
Ry-
8

Esta é uma maneira para vários navegadores de fazer isso no eixo Y; ela funciona em desktops e dispositivos móveis. Testado em OSX e iOS.

var scrollArea = this.querySelector(".scroll-area");
scrollArea.addEventListener("wheel", function() {
    var scrollTop = this.scrollTop;
    var maxScroll = this.scrollHeight - this.offsetHeight;
    var deltaY = event.deltaY;
    if ( (scrollTop >= maxScroll && deltaY > 0) || (scrollTop === 0 && deltaY < 0) ) {
        event.preventDefault();
    }
}, {passive:false});

scrollArea.addEventListener("touchstart", function(event) {
    this.previousClientY = event.touches[0].clientY;
}, {passive:false});

scrollArea.addEventListener("touchmove", function(event) {
    var scrollTop = this.scrollTop;
    var maxScroll = this.scrollHeight - this.offsetHeight;
    var currentClientY = event.touches[0].clientY;
    var deltaY = this.previousClientY - currentClientY;
    if ( (scrollTop >= maxScroll && deltaY > 0) || (scrollTop === 0 && deltaY < 0) ) {
        event.preventDefault();
    }
    this.previousClientY = currentClientY;
}, {passive:false});
Patrick Matte
fonte
Isso funciona muito bem! Uau, exatamente o que eu pesquisei. Ele precisa definitivamente de mais votos positivos.
delato468 01 de
Esta solução funciona muito bem, mas precisa de um pequeno ajuste: é necessário alterar o scrollTop === maxScroll by scrollTop >= maxScroll. Parece ser estranho em um caso, era necessário
tchiot.ludo
7

Eu escrevi resolvendo este problema

  var div;
  div = document.getElementsByClassName('selector')[0];

  div.addEventListener('mousewheel', function(e) {
    if (div.clientHeight + div.scrollTop + e.deltaY >= div.scrollHeight) {
      e.preventDefault();
      div.scrollTop = div.scrollHeight;
    } else if (div.scrollTop + e.deltaY <= 0) {
      e.preventDefault();
      div.scrollTop = 0;
    }
  }, false);
Alex Golovin
fonte
Uma das poucas soluções aqui que funciona sem os elementos da página pulando irritantemente devido ao estouro oculto sendo definido.
Skeptic
+1 esta é uma solução sólida. se você anexar isso a um contêiner rolável, você desejará que o div neste exemplo seja e.currentTarget.
Matt
solução muito limpa. Obrigado
Thinh Bui
Funcionou no Chrome 61.0.3163.100 e Safari 11.0, mas não no FireFox 56.0 (Mac OS El Capitan)
BigJ
Esta é a melhor solução nativa. No entanto, falsenão precisa ser especificado, addEventListenerpois é o padrão. Além disso, as comparações devem ser simples desigualdades ( >e <), não >=ou <=.
Quolonel Perguntas
6

Se entendi sua pergunta corretamente, você deseja evitar a rolagem do conteúdo principal quando o mouse estiver sobre uma div (digamos uma barra lateral). Para isso, a barra lateral não pode ser filha do container de rolagem do conteúdo principal (que era a janela do navegador), para evitar que o evento scroll borbulhe para seu pai.

Isso possivelmente requer algumas alterações de marcação da seguinte maneira:

<div id="wrapper"> 
    <div id="content"> 
    </div> 
</div> 
<div id="sidebar"> 
</div> 

Veja se está funcionando neste exemplo de violino e compare isso com este exemplo de violino, que tem um comportamento de saída do mouse ligeiramente diferente da barra lateral.

Consulte também rolar apenas um div específico com a barra de rolagem principal do navegador .

NGLN
fonte
Nesse caso, a barra lateral está fora da div do wrapper. Então, eu não acho que o pergaminho borbulhará para o pai
Jeevan,
Por "este caso" você quer dizer o seu caso? Se não: você está exatamente certo. Se sim: se sua página rolar no final da rolagem div, a mensagem de rolagem borbulha para o pai div, então acho que sua página e div não são irmãos, mas pai e filho.
NGLN de
Sim, a página é o pai do meu div, então, ao chegar ao fim, ele começa a rolar a página, o que eu não quero.
Jeevan
Se você não pode tornar seu div irmão do conteúdo principal, esse problema não pode ser corrigido apenas com HTML + CSS. É o comportamento padrão.
NGLN
1
Acho que esta é a solução mais eficiente de todas as respostas fornecidas; com uma boa marcação limpa na base de uma página, muito menos hacks CSS e JS são necessários para obter o comportamento desejado.
6

Conforme respondido aqui , a maioria dos navegadores modernos agora oferece suporte à overscroll-behavior: none;propriedade CSS, que evita o encadeamento de rolagem. E é isso, apenas uma linha!

Andriy Stolyar
fonte
2
Essa deve ser a resposta correta em 2020. Para meu caso de uso, a melhor solução era overscroll-behavior: contain;.
Píer
3

isso desativa a rolagem na janela se você inserir o elemento seletor. funciona como amuletos.

elements = $(".selector");

elements.on('mouseenter', function() {
    window.currentScrollTop = $(window).scrollTop();
    window.currentScrollLeft = $(window).scrollTop();
    $(window).on("scroll.prevent", function() {
        $(window).scrollTop(window.currentScrollTop);
        $(window).scrollLeft(window.currentScrollLeft);
    });
});

elements.on('mouseleave', function() {
    $(window).off("scroll.prevent");
});
ceder
fonte
Esta é realmente uma solução de trabalho. Eu precisava disso em js puro, então eu reescrevi. Se alguém precisar, está aqui . Para quem ainda está procurando a solução, você deve verificar minha resposta abaixo .
Andriy Stolyar
2

Você pode desativar a rolagem de toda a página fazendo algo assim, mas exiba a barra de rolagem!

<div onmouseover="document.body.style.overflow='hidden'; document.body.style.position='fixed';" onmouseout="document.body.style.overflow='auto'; document.body.style.position='relative';"></div>
joseantgv
fonte
2
$this.find('.scrollingDiv').on('mousewheel DOMMouseScroll', function (e) {
  var delta = -e.originalEvent.wheelDelta || e.originalEvent.detail;
  var scrollTop = this.scrollTop;
  if((delta < 0 && scrollTop === 0) || (delta > 0 && this.scrollHeight - this.clientHeight - scrollTop === 0)) {
    e.preventDefault();
  }
});
Neil Taylor
fonte
1

Com base na resposta do ceed, aqui está uma versão que permite o aninhamento de elementos protegidos por rolagem. Somente o elemento sobre o qual o mouse está passando irá rolar, e ele rola de maneira bastante suave. Esta versão também é reentrante. Ele pode ser usado várias vezes no mesmo elemento e removerá e reinstalará corretamente os manipuladores.

jQuery.fn.scrollGuard = function() {
    this
        .addClass('scroll-guarding')
        .off('.scrollGuard').on('mouseenter.scrollGuard', function() {
            var $g = $(this).parent().closest('.scroll-guarding');
            $g = $g.length ? $g : $(window);
            $g[0].myCst = $g.scrollTop();
            $g[0].myCsl = $g.scrollLeft();
            $g.off("scroll.prevent").on("scroll.prevent", function() {
                $g.scrollTop($g[0].myCst);
                $g.scrollLeft($g[0].myCsl);
            });
        })
        .on('mouseleave.scrollGuard', function() {
            var $g = $(this).parent().closest('.scroll-guarding');
            $g = $g.length ? $g : $(window);
            $g.off("scroll.prevent");
        });
};

Uma maneira fácil de usar é adicionar uma classe, por exemplo scroll-guard, a todos os elementos da página em que você permite a rolagem. Em seguida, use $('.scroll-guard').scrollGuard()para protegê-los.

d'Artagnan Evergreen Barbosa
fonte
0

Se você aplicar um overflow: hiddenestilo, ele deve desaparecer

editar: na verdade eu li sua pergunta errado, isso só vai esconder a barra de rolagem, mas eu não acho que é isso que você está procurando.

JavaKungFu
fonte
1
Eu preciso da barra de rolagem. Estou falando sobre o comportamento. Quando eu rolar usando meu trackball do mouse ao chegar ao final da rolagem do div, ele não deve rolar a página inteira.
Jeevan
0

Não consegui fazer com que nenhuma das respostas funcionasse no Chrome e no Firefox, então criei este amálgama:

$someElement.on('mousewheel DOMMouseScroll', scrollProtection);

function scrollProtection(event) {
    var $this = $(this);
    event = event.originalEvent;
    var direction = (event.wheelDelta * -1) || (event.detail);
    if (direction < 0) {
        if ($this.scrollTop() <= 0) {
            return false;
        }
    } else {
        if ($this.scrollTop() + $this.innerHeight() >= $this[0].scrollHeight) {
            return false;
        }
    }
}
d.breve
fonte