Impedir a rolagem do elemento pai quando a posição de rolagem do elemento interno atinge a parte superior / inferior?

Eu tenho uma pequena “checkbox de ferramentas flutuante” – uma div com position:fixed; overflow:auto position:fixed; overflow:auto . Funciona muito bem.

Mas quando rolando dentro da checkbox (com a roda do mouse) e alcançando a parte inferior OU superior, o elemento pai “assume” a “solicitação de rolagem“: O documento por trás da checkbox de ferramentas rola.
– Que é chato e não o que o usuário “pediu”.

Estou usando o jQuery e pensei que eu poderia parar esse comportamento com event.stoppropagation ():
$("#toolBox").scroll( function(event){ event.stoppropagation() });

Ele entra na function, mas ainda assim, a propagação acontece de qualquer maneira (o documento rola)
– É surpreendentemente difícil procurar por esse tópico no SO (e no Google), então eu tenho que perguntar:
Como evitar a propagação / subida do evento de rolagem?

Editar:
Solução de trabalho graças a amustill (e Brandon Aaron para o plugin mousewheel aqui:
https://github.com/brandonaaron/jquery-mousewheel/raw/master/jquery.mousewheel.js

 $(".ToolPage").bind('mousewheel', function(e, d) var t = $(this); if (d > 0 && t.scrollTop() === 0) { e.preventDefault(); } else { if (d < 0 && (t.scrollTop() == t.get(0).scrollHeight - t.innerHeight())) { e.preventDefault(); } } }); 

É possível com o uso do plugin Mousewheel de Brandon Aaron.

Aqui está uma demonstração: http://jsbin.com/jivutakama/edit?html,js,output

Eu estou adicionando essa resposta para integridade porque a resposta aceita pelo @amustill não resolve corretamente o problema no Internet Explorer . Por favor, veja os comentários em minha postagem original para detalhes. Além disso, esta solução não requer nenhum plug-in – apenas o jQuery.

Em essência, o código funciona manipulando o evento mousewheel . Cada evento desse tipo contém um wheelDelta igual ao número de px qual ele moverá a área rolável. Se este valor for >0 , então estamos rolando para up . Se o wheelDelta for <0 então estamos rolando down .

FireFox : O FireFox usa DOMMouseScroll como o evento e preenche o originalEvent.detail , cujo +/- é revertido do descrito acima. Geralmente, ele retorna intervalos de 3 , enquanto outros navegadores retornam a rolagem em intervalos de 120 (pelo menos na minha máquina). Para corrigir, simplesmente detectamos e multiplicamos por -40 para normalizar.

A resposta de @ amustill funciona cancelando o evento se a área de rolagem de

já estiver na posição máxima superior ou inferior. No entanto, o Internet Explorer desconsidera o evento cancelado em situações em que o delta é maior que o espaço restante rolável.

Em outras palavras, se você tiver um 500px de 200px

contendo 500px de conteúdo rolável, e o scrollTop atual for 400 , um evento mousewheel que 120px o navegador a rolar 120px mais resultará em

e rolagem, porque 400 + 120 > 500 .

Então - para resolver o problema, temos que fazer algo um pouco diferente, como mostrado abaixo:

O código necessário do jQuery é:

 $(document).on('DOMMouseScroll mousewheel', '.Scrollable', function(ev) { var $this = $(this), scrollTop = this.scrollTop, scrollHeight = this.scrollHeight, height = $this.innerHeight(), delta = (ev.type == 'DOMMouseScroll' ? ev.originalEvent.detail * -40 : ev.originalEvent.wheelDelta), up = delta > 0; var prevent = function() { ev.stopPropagation(); ev.preventDefault(); ev.returnValue = false; return false; } if (!up && -delta > scrollHeight - height - scrollTop) { // Scrolling down, but this will take us past the bottom. $this.scrollTop(scrollHeight); return prevent(); } else if (up && delta > scrollTop) { // Scrolling up, but this will take us past the top. $this.scrollTop(0); return prevent(); } }); 

Em essência, esse código cancela qualquer evento de rolagem que criaria a condição de borda indesejada, em seguida, usa jQuery para definir o scrollTop do

para o valor máximo ou mínimo, dependendo de qual direção o evento mousewheel estava solicitando.

Como o evento é cancelado inteiramente em qualquer um dos casos, ele nunca se propaga para o body e, portanto, resolve o problema no IE, bem como em todos os outros navegadores.

Eu também coloquei um exemplo de trabalho no jsFiddle .

Eu sei que é uma questão bastante antiga, mas como esse é um dos principais resultados no google … Eu tive que de alguma forma cancelar o borbulhamento de scroll sem o jQuery e esse código funciona para mim:

 function preventDefault(e) { e = e || window.event; if (e.preventDefault) e.preventDefault(); e.returnValue = false; } document.getElementById('a').onmousewheel = function(e) { document.getElementById('a').scrollTop -= e. wheelDeltaY; preventDefault(e); } 

EDIT: exemplo CodePen

Para o AngularJS, defini a seguinte diretiva:

 module.directive('isolateScrolling', function () { return { restrict: 'A', link: function (scope, element, attr) { element.bind('DOMMouseScroll', function (e) { if (e.detail > 0 && this.clientHeight + this.scrollTop == this.scrollHeight) { this.scrollTop = this.scrollHeight - this.clientHeight; e.stopPropagation(); e.preventDefault(); return false; } else if (e.detail < 0 && this.scrollTop <= 0) { this.scrollTop = 0; e.stopPropagation(); e.preventDefault(); return false; } }); element.bind('mousewheel', function (e) { if (e.deltaY > 0 && this.clientHeight + this.scrollTop >= this.scrollHeight) { this.scrollTop = this.scrollHeight - this.clientHeight; e.stopPropagation(); e.preventDefault(); return false; } else if (e.deltaY < 0 && this.scrollTop <= 0) { this.scrollTop = 0; e.stopPropagation(); e.preventDefault(); return false; } return true; }); } }; }); 

E, em seguida, adicionou-o ao elemento rolável (o menu suspenso ul):

  

Testado no Chrome e no Firefox. A rolagem suave do Chrome derrota esse truque quando um grande movimento do mouse é feito perto (mas não em) da parte superior ou inferior da região de rolagem.

Há toneladas de perguntas como essa por aí, com muitas respostas, mas não consegui encontrar uma solução satisfatória que não envolvesse events, scripts, plugins, etc. Eu queria manter isso correto em HTML e CSS. Eu finalmente encontrei uma solução que funcionou, embora envolvesse a reestruturação da marcação para quebrar a cadeia de events.


1. problema básico

A input de rolagem (por exemplo: mousewheel) aplicada ao elemento modal se transformará em um elemento ancestral e rolará na mesma direção, se algum desses elementos for rolável:

(Todos os exemplos devem ser visualizados em resoluções de desktop)

https://jsfiddle.net/ybkbg26c/5/

HTML:

 

CSS:

 #modal { position: absolute; height: 100px; width: 100px; top: 20%; left: 20%; overflow-y: scroll; } #parent { height: 4000px; } 

2. Nenhum pergaminho pai na rolagem modal

A razão pela qual o ancestral acaba rolando é porque as bolhas de evento de rolagem e algum elemento na cadeia são capazes de manipulá-lo. Uma maneira de impedir isso é certificar-se de que nenhum dos elementos da cadeia saiba como lidar com o pergaminho. Em termos do nosso exemplo, podemos refatorar a tree para mover o modal para fora do elemento pai. Por razões obscuras, não é suficiente manter o pai e os irmãos DOM modais; o pai deve ser empacotado por outro elemento que estabeleça um novo contexto de empilhamento. Um wrapper absolutamente posicionado em torno do pai pode fazer o truque.

O resultado que obtemos é que, desde que o modal receba o evento de rolagem, o evento não será transmitido para o elemento “pai”.

Normalmente, deve ser possível reprojetar a tree DOM para suportar esse comportamento sem afetar o que o usuário final vê.

https://jsfiddle.net/0bqq31Lv/3/

HTML:

 

CSS (apenas novo):

 #context { position: absolute; overflow-y: scroll; top: 0; bottom: 0; left: 0; right: 0; } 

3. Nenhuma rolagem em qualquer lugar, exceto no modo modal enquanto está em funcionamento

A solução acima ainda permite que o pai receba events de rolagem, desde que eles não sejam interceptados pela janela modal (ou seja, se acionados por mousewheel enquanto o cursor não estiver sobre o modal). Isso às vezes é indesejável e podemos querer proibir toda a rolagem em segundo plano enquanto o modal estiver ativo. Para fazer isso, precisamos inserir um contexto de empilhamento extra que abranja toda a viewport por trás do modal. Podemos fazer isso exibindo uma sobreposição absolutamente posicionada, que pode ser totalmente transparente, se necessário (mas não a visibility:hidden ).

https://jsfiddle.net/0bqq31Lv/2/

HTML:

 

CSS (novo no topo de # 2):

 #overlay { background-color: transparent; position: absolute; top: 0; bottom: 0; left: 0; right: 0; } 

Diretiva Angular JS

Eu tive que envolver uma diretiva angular. O seguinte é um Mashup das outras respostas aqui. testado no Chrome e no Internet Explorer 11.

 var app = angular.module('myApp'); app.directive("preventParentScroll", function () { return { restrict: "A", scope: false, link: function (scope, elm, attr) { elm.bind('mousewheel', onMouseWheel); function onMouseWheel(e) { elm[0].scrollTop -= (e.wheelDeltaY || (e.originalEvent && (e.originalEvent.wheelDeltaY || e.originalEvent.wheelDelta)) || e.wheelDelta || 0); e.stopPropagation(); e.preventDefault(); e.returnValue = false; } } } }); 

Uso

 
...

Espera que isso ajude a próxima pessoa que chegar aqui a partir de uma pesquisa no Google.

Como variante, para evitar problemas de desempenho com o manuseio de scroll ou mousewheel scroll , você pode usar o código abaixo:

css:

 body.noscroll { overflow: hidden; } .scrollable { max-height: 200px; overflow-y: scroll; border: 1px solid #ccc; } 

html:

 
...A bunch of items to make the div scroll...
...A bunch of text to make the body scroll...

js:

 var $document = $(document), $body = $('body'), $scrolable = $('.scrollable'); $scrolable.on({ 'mouseenter': function () { // add hack class to prevent workspace scroll when scroll outside $body.addClass('noscroll'); }, 'mouseleave': function () { // remove hack class to allow scroll $body.removeClass('noscroll'); } }); 

Exemplo de trabalho: http://jsbin.com/damuwinarata/4

Usando as propriedades de rolagem do elemento nativo com o valor delta do plug-in mousewheel:

 $elem.on('mousewheel', function (e, delta) { // Restricts mouse scrolling to the scrolling range of this element. if ( this.scrollTop < 1 && delta > 0 || (this.clientHeight + this.scrollTop) === this.scrollHeight && delta < 0 ) { e.preventDefault(); } }); 

Todas as soluções dadas neste tópico não mencionam uma forma existente – e nativa – de resolver este problema sem reordenar o DOM e / ou utilizar truques de prevenção de events. Mas há uma boa razão: esse caminho é proprietário – e está disponível apenas na plataforma web da Microsoft. Citando o MSDN :

-ms- propriedade de encadeamento de rolagem – especifica o comportamento de rolagem que ocorre quando um usuário atinge o limite de rolagem durante uma manipulação. Valores de propriedade:

encadeada – valor inicial. O elemento pai rolável mais próximo começa a rolar quando o usuário atinge um limite de rolagem durante uma manipulação. Nenhum efeito de ressalto é mostrado.

none – Um efeito de rejeição é exibido quando o usuário atinge um limite de rolagem durante uma manipulação.

Concedido, esta propriedade é suportada apenas no IE10 + / Edge. Ainda assim, aqui está uma citação :

Para dar uma ideia do quão popular o encadeamento de rolagem preventivo pode ser, de acordo com minha pesquisa rápida de http-archive “-ms-scroll-chaining: none” é usado em 0,4% das 300K páginas principais, apesar de ser limitado em funcionalidade e suportado apenas IE / Edge.

E agora boas notícias, pessoal! A partir do Chrome 63, finalmente temos uma cura nativa para as plataformas baseadas no Blink – e isso é o Chrome (obviamente) e o Android WebView (em breve).

Citando o artigo de introdução :

A propriedade overscroll-behavior é um novo recurso CSS que controla o comportamento do que acontece quando você rola demais em um contêiner (incluindo a própria página). Você pode usá-lo para cancelar o encadeamento de rolagem, desabilitar / personalizar a ação “puxar para atualizar”, desabilitar os efeitos de “rubberbanding” no iOS (quando o Safari […] implementa o comportamento de excesso de rolagem).

A propriedade aceita três valores possíveis:

auto – padrão. Rolos originados no elemento podem se propagar para elementos ancestrais.

conter – evita o encadeamento de rolagem. Os pergaminhos não se propagam para os ancestrais, mas os efeitos locais dentro do nó são mostrados. Por exemplo, o efeito de brilho overscroll no Android ou o efeito rubberbanding no iOS que notifica o usuário quando atingem um limite de rolagem. Nota: usando overscroll-behavior: conter no elemento html evita ações de navegação overscroll.

none – o mesmo que contém, mas também evita efeitos overscroll dentro do próprio nó (por exemplo, brilho overscroll do Android ou rubberbanding iOS).

[…] A melhor parte é que usar o comportamento overscroll não afeta negativamente o desempenho da página, como os hacks mencionados na introdução!

Aqui está esse recurso em ação . E aqui está o documento correspondente do Módulo CSS .

ATUALIZAÇÃO: O Firefox, desde a versão 59, juntou-se ao clube, e espera-se que o MS Edge implemente esse recurso na versão 18. Aqui está a canalização correspondente.

Caso alguém ainda esteja procurando uma solução para isso, o seguinte plugin faz o trabalho http://mohammadyounes.github.io/jquery-scrollLock/

Aborda totalmente a questão de bloquear a rolagem da roda do mouse dentro de um determinado contêiner, impedindo a propagação para o elemento pai.

Não altera a velocidade de rolagem da roda, a experiência do usuário não será afetada. e você obtém o mesmo comportamento, independentemente da velocidade de rolagem vertical da roda do mouse do SO (no Windows, ela pode ser definida como uma canvas ou uma linha até 100 linhas por corte).

Demonstração: http://mohammadyounes.github.io/jquery-scrollLock/example/

Fonte: https://github.com/MohammadYounes/jquery-scrollLock

Aqui está uma versão simples de JavaScript:

 function scroll(e) { var delta = (e.type === "mousewheel") ? e.wheelDelta : e.detail * -40; if (delta < 0 && (this.scrollHeight - this.offsetHeight - this.scrollTop) <= 0) { this.scrollTop = this.scrollHeight; e.preventDefault(); } else if (delta > 0 && delta > this.scrollTop) { this.scrollTop = 0; e.preventDefault(); } } document.querySelectorAll(".scroller").addEventListener("mousewheel", scroll); document.querySelectorAll(".scroller").addEventListener("DOMMouseScroll", scroll); 

A resposta de Amustill como um manipulador de nocaute:

 ko.bindingHandlers.preventParentScroll = { init: function (element, valueAccessor, allBindingsAccessor, context) { $(element).mousewheel(function (e, d) { var t = $(this); if (d > 0 && t.scrollTop() === 0) { e.preventDefault(); } else { if (d < 0 && (t.scrollTop() == t.get(0).scrollHeight - t.innerHeight())) { e.preventDefault(); } } }); } }; 

Isso realmente funciona no AngularJS. Testado no Chrome e no Firefox.

 .directive('stopScroll', function () { return { restrict: 'A', link: function (scope, element, attr) { element.bind('mousewheel', function (e) { var $this = $(this), scrollTop = this.scrollTop, scrollHeight = this.scrollHeight, height = $this.height(), delta = (e.type == 'DOMMouseScroll' ? e.originalEvent.detail * -40 : e.originalEvent.wheelDelta), up = delta > 0; var prevent = function() { e.stopPropagation(); e.preventDefault(); e.returnValue = false; return false; }; if (!up && -delta > scrollHeight - height - scrollTop) { // Scrolling down, but this will take us past the bottom. $this.scrollTop(scrollHeight); return prevent(); } else if (up && delta > scrollTop) { // Scrolling up, but this will take us past the top. $this.scrollTop(0); return prevent(); } }); } }; }) 

o método acima não é tão natural, depois de algum googling eu acho uma solução mais legal, e não há necessidade de jQuery. veja [1] e demonstração [2].

  var element = document.getElementById('uf-notice-ul'); var isMacWebkit = (navigator.userAgent.indexOf("Macintosh") !== -1 && navigator.userAgent.indexOf("WebKit") !== -1); var isFirefox = (navigator.userAgent.indexOf("firefox") !== -1); element.onwheel = wheelHandler; // Future browsers element.onmousewheel = wheelHandler; // Most current browsers if (isFirefox) { element.scrollTop = 0; element.addEventListener("DOMMouseScroll", wheelHandler, false); } // prevent from scrolling parrent elements function wheelHandler(event) { var e = event || window.event; // Standard or IE event object // Extract the amount of rotation from the event object, looking // for properties of a wheel event object, a mousewheel event object // (in both its 2D and 1D forms), and the Firefox DOMMouseScroll event. // Scale the deltas so that one "click" toward the screen is 30 pixels. // If future browsers fire both "wheel" and "mousewheel" for the same // event, we'll end up double-counting it here. Hopefully, however, // cancelling the wheel event will prevent generation of mousewheel. var deltaX = e.deltaX * -30 || // wheel event e.wheelDeltaX / 4 || // mousewheel 0; // property not defined var deltaY = e.deltaY * -30 || // wheel event e.wheelDeltaY / 4 || // mousewheel event in Webkit (e.wheelDeltaY === undefined && // if there is no 2D property then e.wheelDelta / 4) || // use the 1D wheel property e.detail * -10 || // Firefox DOMMouseScroll event 0; // property not defined // Most browsers generate one event with delta 120 per mousewheel click. // On Macs, however, the mousewheels seem to be velocity-sensitive and // the delta values are often larger multiples of 120, at // least with the Apple Mouse. Use browser-testing to defeat this. if (isMacWebkit) { deltaX /= 30; deltaY /= 30; } e.currentTarget.scrollTop -= deltaY; // If we ever get a mousewheel or wheel event in (a future version of) // Firefox, then we don't need DOMMouseScroll anymore. if (isFirefox && e.type !== "DOMMouseScroll") { element.removeEventListener("DOMMouseScroll", wheelHandler, false); } // Don't let this event bubble. Prevent any default action. // This stops the browser from using the mousewheel event to scroll // the document. Hopefully calling preventDefault() on a wheel event // will also prevent the generation of a mousewheel event for the // same rotation. if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); e.cancelBubble = true; // IE events e.returnValue = false; // IE events return false; } 

Para aqueles que usam o MooTools, aqui está o código equivalente:

  'mousewheel': function(event){ var height = this.getSize().y; height -= 2; // Not sure why I need this bodge if ((this.scrollTop === (this.scrollHeight - height) && event.wheel < 0) || (this.scrollTop === 0 && event.wheel > 0)) { event.preventDefault(); } 

Tenha em mente que eu, como alguns outros, tive que ajustar um valor por um par de px, isso é o que a altura – = 2 é para.

Basicamente a principal diferença é que no MooTools, a informação delta vem de event.wheel ao invés de um parâmetro extra passado para o evento.

Além disso, eu tive problemas se eu vinculasse esse código a qualquer coisa (event.target.scrollHeight para uma function ligada não é igual a this.scrollHeight para uma não vinculada)

Espero que isso ajude alguém tanto quanto este post me ajudou;)

meu plugin jQuery:

 $('.child').dontScrollParent(); $.fn.dontScrollParent = function() { this.bind('mousewheel DOMMouseScroll',function(e) { var delta = e.originalEvent.wheelDelta || -e.originalEvent.detail; if (delta > 0 && $(this).scrollTop() < = 0) return false; if (delta < 0 && $(this).scrollTop() >= this.scrollHeight - $(this).height()) return false; return true; }); } 

Novo web dev aqui. Isso funcionou como um charme para mim no IE e no Chrome.

 static preventScrollPropagation(e: HTMLElement) { e.onmousewheel = (ev) => { var preventScroll = false; var isScrollingDown = ev.wheelDelta < 0; if (isScrollingDown) { var isAtBottom = e.scrollTop + e.clientHeight == e.scrollHeight; if (isAtBottom) { preventScroll = true; } } else { var isAtTop = e.scrollTop == 0; if (isAtTop) { preventScroll = true; } } if (preventScroll) { ev.preventDefault(); } } } 

Não deixe o número de linhas enganá-lo, é bastante simples - apenas um pouco detalhado para a legibilidade (auto documentando código ftw certo?)

Também devo mencionar que a linguagem aqui é TypeScript , mas como sempre, é fácil convertê-lo em JS.

Eu chamei isso da biblioteca escolhida: https://github.com/harvesthq/chosen/blob/master/coffee/chosen.jquery.coffee

 function preventParentScroll(evt) { var delta = evt.deltaY || -evt.wheelDelta || (evt && evt.detail) if (delta) { evt.preventDefault() if (evt.type == 'DOMMouseScroll') { delta = delta * 40 } fakeTable.scrollTop = delta + fakeTable.scrollTop } } var el = document.getElementById('some-id') el.addEventListener('mousewheel', preventParentScroll) el.addEventListener('DOMMouseScroll', preventParentScroll) 

Isso funciona para mim.

Eu odeio necro postar, mas eu estava procurando por isso para MooTools e este foi o primeiro que surgiu. O exemplo original do MooTools funcionaria com a rolagem para cima, mas não rolando para baixo, então decidi escrever este.


 var stopScroll = function (e) { var scrollTo = null; if (e.event.type === 'mousewheel') { scrollTo = (e.event.wheelDelta * -1); } else if (e.event.type === 'DOMMouseScroll') { scrollTo = 40 * e.event.detail; } if (scrollTo) { e.preventDefault(); this.scrollTo(0, scrollTo + this.scrollTop); } return false; }; 

Uso:

 (function)($){ window.addEvent('domready', function(){ $$('.scrollable').addEvents({ 'mousewheel': stopScroll, 'DOMMouseScroll': stopScroll }); }); })(document.id); 

Plugin jQuery com emulação de rolagem natural para o Internet Explorer

  $.fn.mousewheelStopPropagation = function(options) { options = $.extend({ // defaults wheelstop: null // Function }, options); // Compatibilities var isMsIE = ('Microsoft Internet Explorer' === navigator.appName); var docElt = document.documentElement, mousewheelEventName = 'mousewheel'; if('onmousewheel' in docElt) { mousewheelEventName = 'mousewheel'; } else if('onwheel' in docElt) { mousewheelEventName = 'wheel'; } else if('DOMMouseScroll' in docElt) { mousewheelEventName = 'DOMMouseScroll'; } if(!mousewheelEventName) { return this; } function mousewheelPrevent(event) { event.preventDefault(); event.stopPropagation(); if('function' === typeof options.wheelstop) { options.wheelstop(event); } } return this.each(function() { var _this = this, $this = $(_this); $this.on(mousewheelEventName, function(event) { var origiEvent = event.originalEvent; var scrollTop = _this.scrollTop, scrollMax = _this.scrollHeight - $this.outerHeight(), delta = -origiEvent.wheelDelta; if(isNaN(delta)) { delta = origiEvent.deltaY; } var scrollUp = delta < 0; if((scrollUp && scrollTop <= 0) || (!scrollUp && scrollTop >= scrollMax)) { mousewheelPrevent(event); } else if(isMsIE) { // Fix Internet Explorer and emulate natural scrolling var animOpt = { duration:200, easing:'linear' }; if(scrollUp && -delta > scrollTop) { $this.stop(true).animate({ scrollTop:0 }, animOpt); mousewheelPrevent(event); } else if(!scrollUp && delta > scrollMax - scrollTop) { $this.stop(true).animate({ scrollTop:scrollMax }, animOpt); mousewheelPrevent(event); } } }); }); }; 

https://github.com/basselin/jquery-mousewheel-stop-propagation/blob/master/mousewheelStopPropagation.js

A melhor solução que pude encontrar foi ouvir o evento de rolagem na janela e definir o scrollTop para o scrollTop anterior se o div filho estivesse visível.

 prevScrollPos = 0 $(window).scroll (ev) -> if $('#mydiv').is(':visible') document.body.scrollTop = prevScrollPos else prevScrollPos = document.body.scrollTop 

Há um piscar de olhos no plano de fundo da div infantil se você triggersr muitos events de rolagem, portanto, isso pode ser ajustado, mas dificilmente é percebido e é suficiente para o meu caso de uso.

Eu tenho uma situação semelhante e aqui está como eu resolvi isso:
Todos os meus elementos roláveis ​​obtêm a class rolável .

 $(document).on('wheel', '.scrollable', function(evt) { var offsetTop = this.scrollTop + parseInt(evt.originalEvent.deltaY, 10); var offsetBottom = this.scrollHeight - this.getBoundingClientRect().height - offsetTop; if (offsetTop < 0 || offsetBottom < 0) { evt.preventDefault(); } else { evt.stopImmediatePropagation(); } }); 

stopImmediatePropagation () certifica-se de não rolar a área de rolagem pai da área filha rolável.

Aqui está uma implementação JS vanilla dele: http://jsbin.com/lugim/2/edit?js,output

Don’t use overflow: hidden; on body . It automatically scrolls everything to the top. There’s no need for JavaScript either. Make use of overflow: auto; :

HTML Structure

 
lengthy content here

Styling

 .overlay{ position: fixed; top: 0px; left: 0px; right: 0px; bottom: 0px; background-color: rgba(0, 0, 0, 0.8); .overlay-content { height: 100%; overflow: scroll; } } .background-content{ height: 100%; overflow: auto; } 

Play with the demo here .

Check out Leland Kwong’s code.

Basic idea is to bind the wheeling event to the child element, and then use the native javascript property scrollHeight and the jquery property outerHeight of the child element to detect the end of the scroll, upon which return false to the wheeling event to prevent any scrolling.

 var scrollableDist,curScrollPos,wheelEvent,dY; $('#child-element').on('wheel', function(e){ scrollableDist = $(this)[0].scrollHeight - $(this).outerHeight(); curScrollPos = $(this).scrollTop(); wheelEvent = e.originalEvent; dY = wheelEvent.deltaY; if ((dY>0 && curScrollPos >= scrollableDist) || (dY<0 && curScrollPos < = 0)) { return false; } }); 

There’s also a funny trick to lock the parent’s scrollTop when mouse hovers over a scrollable element. This way you don’t have to implement your own wheel scrolling.

Here’s an example for preventing document scroll, but it can be adjusted for any element.

 scrollable.mouseenter(function () { var scroll = $(document).scrollTop(); $(document).on('scroll.trap', function () { if ($(document).scrollTop() != scroll) $(document).scrollTop(scroll); }); }); scrollable.mouseleave(function () { $(document).off('scroll.trap'); }); 

MK offered a great plugin in his answer. Plugin can be found here . However, for the sake of completion, I thought it’d be a good idea to put it together in one answer for AngularJS.

  1. Start by injecting the bower or npm (whichever is preferred)

     bower install jquery-scrollLock --save npm install jquery-scroll-lock --save 
  2. Add the following directive. I am choosing to add it as an attribute

     (function() { 'use strict'; angular .module('app') .directive('isolateScrolling', isolateScrolling); function isolateScrolling() { return { restrict: 'A', link: function(sc, elem, attrs) { $('.scroll-container').scrollLock(); } } } })(); 
  3. And the important piece the plugin fails to document in their website is the HTML structure that it must follow.

     
    ... whatever ...

The attribute isolate-scrolling must contain the scrollable class and it all needs to be inside the scroll-container class or whatever class you choose and the locked class must be cascaded.

It is worth to mention that with modern frameworks like reactJS, AngularJS, VueJS, etc, there are easy solutions for this problem, when dealing with fixed position elements. Examples are side panels or overlaid elements.

The technique is called a “Portal”, which means that one of the components used in the app, without the need to actually extract it from where you are using it, will mount its children at the bottom of the body element, outside of the parent you are trying to avoid scrolling.

Note that it will not avoid scrolling the body element itself. You can combine this technique and mounting your app in a scrolling div to achieve the expected result.

Example Portal implementation in React’s material-ui: https://material-ui-next.com/api/portal/

Simple solution with mouseweel event:

 $('.element').bind('mousewheel', function(e, d) { console.log(this.scrollTop,this.scrollHeight,this.offsetHeight,d); if((this.scrollTop === (this.scrollHeight - this.offsetHeight) && d < 0) || (this.scrollTop === 0 && d > 0)) { e.preventDefault(); } }); 

You can try it this way:

 $('#element').on('shown', function(){ $('body').css('overflow-y', 'hidden'); $('body').css('margin-left', '-17px'); }); $('#element').on('hide', function(){ $('body').css('overflow-y', 'scroll'); $('body').css('margin-left', '0px'); });