¿Evita el desplazamiento del elemento principal cuando la posición de desplazamiento del elemento interno alcanza la parte superior / inferior?

Tengo una pequeña “caja de herramientas flotante” – un div con position:fixed; overflow:auto position:fixed; overflow:auto . Funciona bien

Pero cuando se desplaza dentro de esa casilla (con la rueda del mouse) y alcanza la parte inferior O superior, el elemento principal “toma el control” de la “solicitud de desplazamiento”: El documento que se encuentra detrás de la caja de herramientas se desplaza.
– Que es molesto y no lo que el usuario “pidió”.

Estoy usando jQuery y pensé que podría detener este comportamiento con event.stoppropagation ():
$("#toolBox").scroll( function(event){ event.stoppropagation() });

Ingresa a la función, pero aún así, la propagación ocurre de todos modos (el documento se desplaza)
– Es sorprendentemente difícil buscar este tema en SO (y Google), así que tengo que preguntar:
¿Cómo prevenir la propagación / burbujeo del evento scroll?

Editar:
Solución de trabajo gracias a amustill (y a Brandon Aaron por el plugin mousewheel aquí:
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(); } } }); 

Es posible con el uso del complemento Brandon Aaron’s Mousewheel .

Aquí hay una demostración: http://jsbin.com/jivutakama/edit?html,js,output

Estoy agregando esta respuesta para que esté completa porque la respuesta aceptada por @amustill no resuelve el problema correctamente en Internet Explorer . Por favor, vea los comentarios en mi publicación original para más detalles. Además, esta solución no requiere complementos, solo jQuery.

En esencia, el código funciona manejando el evento mousewheel . Cada evento de este tipo contiene una wheelDelta igual a la cantidad de px que moverá el área desplazable. Si este valor es >0 , entonces nos estamos desplazando up . Si el wheelDelta es <0 entonces estamos desplazándonos down .

FireFox : FireFox utiliza DOMMouseScroll como el evento y completa originalEvent.detail , cuyo +/- se invierte de lo descrito anteriormente. Por lo general, devuelve intervalos de 3 , mientras que otros exploradores vuelven a desplazarse en intervalos de 120 (al menos en mi máquina). Para corregirlo, simplemente lo detectamos y multiplicamos por -40 para normalizarlo.

La respuesta de @ amustill funciona cancelando el evento si el área desplazable del

ya está en la posición máxima superior o inferior. Sin embargo, Internet Explorer no tiene en cuenta el evento cancelado en situaciones en las que el delta es más grande que el espacio desplazable restante.

En otras palabras, si tiene un

200px alto que contiene 500px de contenido desplazable, y el scrollTop actual es 400 , un evento mousewheel que le dice al navegador que se desplace 120px más resultará en el

y el desplazamiento, porque 400 + 120 > 500 .

Entonces, para resolver el problema, tenemos que hacer algo ligeramente diferente, como se muestra a continuación:

El código jQuery requerido es:

 $(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(); } }); 

En esencia, este código cancela cualquier evento de desplazamiento que crearía la condición de borde no deseado, luego usa jQuery para establecer el scrollTop de

en el valor máximo o mínimo, dependiendo de la dirección que mousewheel evento mousewheel .

Debido a que el evento se cancela por completo en cualquier caso, nunca se propaga al body en absoluto, y por lo tanto resuelve el problema en IE, así como en todos los otros navegadores.

También puse un ejemplo de trabajo en jsFiddle .

Sé que es una pregunta bastante antigua, pero dado que este es uno de los mejores resultados en google … tuve que cancelar de alguna manera el burbujeo de scroll sin jQuery y este código funciona para mí:

 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: ejemplo de CodePen

Para AngularJS, definí la siguiente directiva:

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

Y luego lo agregué al elemento desplazable (el menú desplegable ul):

  

Probado en Chrome y Firefox. El desplazamiento suave de Chrome derrota este truco cuando se hace un gran movimiento de la rueda del mouse cerca (pero no en) la parte superior o inferior de la región de desplazamiento.

Hay un montón de preguntas como esta, con muchas respuestas, pero no pude encontrar una solución satisfactoria que no involucrara eventos, scripts, complementos, etc. Quería mantenerla en línea recta en HTML y CSS. Finalmente encontré una solución que funcionó, aunque implicó reestructurar el marcado para romper la cadena de eventos.


1. Problema básico

La entrada de desplazamiento (es decir, la rueda del mouse) aplicada al elemento modal se extenderá a un elemento ancestro y lo desplazará en la misma dirección, si algún elemento es desplazable:

(Todos los ejemplos están destinados a ser vistos en resoluciones de escritorio)

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. Sin desplazamiento primario en desplazamiento modal

La razón por la que el ancestro termina desplazándose es porque el evento de desplazamiento burbujea y algún elemento de la cadena puede manejarlo. Una forma de detener esto es asegurarse de que ninguno de los elementos de la cadena sepa cómo manejar el desplazamiento. En términos de nuestro ejemplo, podemos refactorizar el árbol para mover el modal del elemento padre. Por razones oscuras, no es suficiente mantener al padre y a los hermanos DOM modales; el padre debe estar envuelto por otro elemento que establezca un nuevo contexto de astackmiento. Un contenedor absolutamente posicionado alrededor del padre puede hacer el truco.

El resultado que obtenemos es que, siempre que el modal reciba el evento de desplazamiento, el evento no pasará al elemento “principal”.

Por lo general, debería ser posible rediseñar el árbol DOM para admitir este comportamiento sin afectar lo que ve el usuario final.

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

HTML:

 

CSS (solo nuevo):

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

3. No desplazarse a ningún lado excepto en modo modal mientras está activo

La solución anterior aún le permite al padre recibir eventos de desplazamiento, siempre y cuando no sean interceptados por la ventana modal (es decir, si se desencadena con la rueda del mouse mientras el cursor no está sobre el modal). Esto a veces es indeseable y es posible que deseemos prohibir todo desplazamiento en segundo plano mientras el modal está activo. Para hacer eso, necesitamos insertar un contexto de astackmiento adicional que abarque toda la ventana gráfica detrás del modal. Podemos hacerlo mostrando una superposición totalmente posicionada, que puede ser completamente transparente si es necesario (pero no visibility:hidden ).

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

HTML:

 

CSS (nuevo en la parte superior de la n. ° 2):

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

Directiva Angular JS

Tenía que ajustar una directiva angular. El siguiente es un Mashup de las otras respuestas aquí. probado en Chrome e 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 esto ayude a la siguiente persona que llega aquí desde una búsqueda en Google.

Como variante, para evitar problemas de rendimiento con el scroll o la manipulación de la mousewheel , puede usar el código siguiente:

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

Ejemplo de trabajo: http://jsbin.com/damuwinarata/4

Usar propiedades de desplazamiento de elemento nativo con el valor delta del complemento 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 las soluciones proporcionadas en este hilo no mencionan una forma existente (y nativa) de resolver este problema sin reordenar DOM y / o utilizar trucos para evitar eventos. Pero hay una buena razón: de esta manera es patentada, y solo está disponible en la plataforma web de MS. Citando MSDN :

propiedad -ms-scroll-encadenamiento : especifica el comportamiento de desplazamiento que se produce cuando un usuario toca el límite de desplazamiento durante una manipulación. Valores de propiedad:

encadenado – Valor inicial. El elemento principal desplazable más cercano comienza a desplazarse cuando el usuario toca un límite de desplazamiento durante una manipulación. No se muestra ningún efecto de rebote.

none : se muestra un efecto de rebote cuando el usuario toca un límite de desplazamiento durante una manipulación.

Por supuesto, esta propiedad solo es compatible con IE10 + / Edge. Aún así, aquí hay una cita contundente :

Para darle una idea de lo popular que puede ser prevenir el encadenamiento de desplazamiento, de acuerdo con mi búsqueda rápida de archivo http “-ms-scroll-encadenamiento: ninguno” se usa en 0.4% de las 300,000 páginas principales a pesar de tener una funcionalidad limitada y solo soporte en IE / Edge.

Y ahora buenas noticias, ¡a todos! A partir de Chrome 63, finalmente tenemos una cura nativa para las plataformas basadas en Blink también, y eso es tanto Chrome (obviamente) como Android WebView (pronto).

Citando el artículo introductor :

La propiedad de comportamiento de sobrerrecorrido es una nueva característica de CSS que controla el comportamiento de lo que sucede cuando desplaza en exceso un contenedor (incluida la página misma). Puede usarlo para cancelar el encadenamiento de desplazamiento, deshabilitar / personalizar la acción de pull-to-refresh, deshabilitar los efectos de rubberbanding en iOS (cuando Safari implementa comportamiento overscroll) y más. […]

La propiedad toma tres valores posibles:

auto – Predeterminado. Los rollos que se originan en el elemento pueden propagarse a elementos ancestrales.

contener – evita el encadenamiento de desplazamiento. Los rollos no se propagan a los antepasados, pero se muestran los efectos locales dentro del nodo. Por ejemplo, el efecto de destello del sobrerrollamiento en Android o el efecto rubberbanding en iOS que notifica al usuario cuando han tocado un límite de desplazamiento. Nota: el uso de overscroll-behavior: contains en el elemento html evita las acciones de navegación de sobredesplotación.

ninguno : lo mismo que contener, pero también previene los efectos de sobrerrecorrido dentro del propio nodo (p. ej., resplandor de desbordamiento de Android o banda de goma de iOS).

[…] ¡ La mejor parte es que el uso de overscroll-behavior no afecta negativamente el rendimiento de la página, como los hacks mencionados en la introducción!

Aquí está esta característica en acción . Y aquí está el documento del Módulo CSS correspondiente.

ACTUALIZACIÓN: Firefox, desde la versión 59, se ha unido al club, y se espera que MS Edge implemente esta característica en la versión 18. Aquí está el caniusage correspondiente.

En caso de que alguien todavía esté buscando una solución para esto, el siguiente complemento hace el trabajo http://mohammadyounes.github.io/jquery-scrollLock/

Aborda completamente el problema de bloquear el desplazamiento de la rueda del mouse dentro de un contenedor determinado, lo que impide que se propague al elemento principal.

No cambia la velocidad de desplazamiento de la rueda, la experiencia del usuario no se verá afectada. y obtiene el mismo comportamiento independientemente de la velocidad de desplazamiento vertical de la rueda del mouse del sistema operativo (en Windows se puede establecer en una pantalla o una línea de hasta 100 líneas por muesca).

Demostración: http://mohammadyounes.github.io/jquery-scrollLock/example/

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

Aquí hay una versión de JavaScript simple:

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

la respuesta de amustill como controlador de knockout:

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

Esto realmente funciona en AngularJS. Probado en Chrome y 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(); } }); } }; }) 

el método anterior no es tan natural, después de buscar en Google encuentro una solución más agradable y no necesito jQuery. ver [1] y demostración [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 aquellos que usan MooTools, aquí hay un 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(); } 

Tenga en cuenta que yo, como algunos otros, tuve que modificar un valor por un par de px, para eso es la altura – = 2.

Básicamente, la principal diferencia es que en MooTools, la información delta proviene de event.wheel en lugar de un parámetro adicional que se pasa al evento.

Además, tuve problemas si ligaba este código a cualquier cosa (event.target.scrollHeight para una función enlazada no es igual a this.scrollHeight para una no vinculada)

Espero que esto ayude a alguien tanto como este post me ayudó;)

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

Nuevo desarrollador web aquí. Esto funcionó como un encanto para mí en IE y 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(); } } } 

No dejes que la cantidad de líneas te engañe, es bastante simple, solo un poco detallado para la legibilidad (código de auto documentación, ¿verdad?)

También debo mencionar que el lenguaje aquí es TypeScript , pero como siempre, es sencillo convertirlo a JS.

Lo solté de la biblioteca elegida: 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) 

Esto funciona para mí

Odio publicar necro pero estaba buscando esto para MooTools y este fue el primero que apareció. El ejemplo original de MooTools funcionaría al desplazarse hacia arriba, pero no al desplazarse hacia abajo, así que decidí escribir 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 con emular el desplazamiento natural para 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

The best solution I could find was listening to the scroll event on the window and set the scrollTop to the previous scrollTop if the child div was visible.

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

There is a flicker in the background of the child div if you fire a lot of scroll events, so this could be tweaked, but it is hardly noticed and it was sufficient for my use case.

I have a similar situation and here’s how i solved it:
All my scrollable elements get the class scrollable .

 $(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() makes sure not to scroll parent scrollable area from scrollable child area.

Here's a vanilla JS implementation of it: 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

Estilo

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