Empêcher le défilement de l’élément parent lorsque la position de défilement de l’élément interne atteint le haut / bas?

J’ai une petite “boîte à outils flottante” – une div avec position:fixed; overflow:auto position:fixed; overflow:auto . Fonctionne très bien.

Mais lorsque vous faites défiler cette zone (avec la molette de la souris) et que vous atteignez le haut ou le bas, l’élément parent “reprend” la “demande de défilement”: le document situé derrière la boîte à outils défile.
– Ce qui est agaçant et non pas ce que l’utilisateur “a demandé”.

J’utilise jQuery et je pensais pouvoir arrêter ce comportement avec event.stoppropagation ():
$("#toolBox").scroll( function(event){ event.stoppropagation() });

Il entre dans la fonction, mais la propagation se produit quand même (le document défile)
– Il est étonnamment difficile de rechercher ce sujet sur SO (et Google), alors je dois demander:
Comment empêcher la propagation / le bullage de l’événement scroll?

Modifier:
Solution de travail grâce à amustill (et Brandon Aaron pour le plug-in mousewheel ici:
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(); } } }); 

C’est possible avec l’utilisation du plugin Mousewheel de Brandon Aaron.

Voici une démonstration: http://jsbin.com/jivutakama/edit?html,js,output

J’ajoute cette réponse pour être complet car la réponse acceptée par @amustill ne résout pas correctement le problème dans Internet Explorer . S’il vous plaît voir les commentaires dans mon message d’origine pour plus de détails. En outre, cette solution ne nécessite aucun plug-in – uniquement jQuery.

Essentiellement, le code fonctionne en gérant l’événement mousewheel . Chacun de ces événements contient un wheelDelta égal au nombre de px lesquels il va déplacer la zone de défilement. Si cette valeur est >0 , alors nous faisons défiler up . Si le wheelDelta est <0 alors nous faisons défiler down .

FireFox : FireFox utilise DOMMouseScroll comme événement et remplit originalEvent.detail , dont +/- est inversé par rapport à ce qui est décrit ci-dessus. Il retourne généralement des intervalles de 3 , tandis que les autres navigateurs renvoient le défilement à intervalles de 120 (au moins sur ma machine). Pour corriger, nous le détectons simplement et multiplions par -40 pour normaliser.

La réponse de @amustill fonctionne en annulant l'événement si la zone de défilement de

est déjà en haut ou en bas. Toutefois, Internet Explorer ignore l'événement annulé dans les situations où le delta est supérieur à l'espace défilant restant.

En d'autres termes, si vous avez un

200px hauteur contenant 500px de contenu défilable et que le scrollTop actuel est 400 , un événement mousewheel qui indique au navigateur de faire défiler 120px entraînera à la fois le

et le défilement, car 400 + 120 > 500 .

Donc, pour résoudre le problème, nous devons faire quelque chose de légèrement différent, comme indiqué ci-dessous:

Le code jQuery requirejs est:

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

Essentiellement, ce code annule tout événement de défilement qui créerait la condition de bord indésirable, puis utilise jQuery pour définir le scrollTop du

sur la valeur maximale ou minimale, selon la direction mousewheel événement Mousewheel.

Étant donné que l'événement est annulé dans tous les cas, il ne se propage jamais sur le body et résout donc le problème dans IE, ainsi que dans tous les autres navigateurs.

J'ai également mis en place un exemple de travail sur jsFiddle .

Je sais que c’est une assez vieille question, mais comme c’est l’un des meilleurs résultats de Google … j’ai dû annuler en quelque sorte le défilement de défilement sans jQuery et ce code fonctionne pour moi:

 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: Exemple CodePen

Pour AngularJS, j’ai défini la directive suivante:

 module.directive('isolateScrolling', function () { return { ressortingct: '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; }); } }; }); 

Et puis l'a ajouté à l'élément scrollable (le menu déroulant ul):

  

Testé sur Chrome et Firefox. Le défilement régulier de Chrome annule ce piratage lorsqu'un grand mouvement de molette est effectué près (mais pas) du haut ou du bas de la zone de défilement.

Il y a des tonnes de questions comme celle-ci, avec beaucoup de réponses, mais je n’ai pas pu trouver de solution satisfaisante qui n’implique pas d’événements, de scripts, de plugins, etc. J’ai finalement trouvé une solution qui fonctionnait bien, mais cela impliquait la restructuration du balisage pour briser la chaîne d’événements.


1. problème de base

L’entrée de défilement (par exemple: molette de la souris) appliquée à l’élément modal se répercute dans un élément ancêtre et la fait défiler dans le même sens, si un tel élément est défilable:

(Tous les exemples sont destinés à être visualisés sur des résolutions de bureau)

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. Aucun parent ne défile sur le défilement modal

La raison pour laquelle l’ancêtre finit par faire défiler est que l’événement de défilement fait des bulles et qu’un élément de la chaîne est capable de le gérer. Une façon d’arrêter cela est de s’assurer qu’aucun des éléments de la chaîne ne sait comment gérer le parchemin. En ce qui concerne notre exemple, nous pouvons modifier l’arborescence pour déplacer le modal de l’élément parent. Pour des raisons obscures, il ne suffit pas de garder les parents et les frères modaux du DOM; le parent doit être enveloppé par un autre élément qui établit un nouveau contexte d’emstackment. Un wrapper absolument positionné autour du parent peut faire l’affaire.

Le résultat que nous obtenons est que tant que le modal reçoit l’événement de défilement, l’événement ne sera pas dirigé vers l’élément “parent”.

Il est généralement possible de repenser l’arborescence DOM pour prendre en charge ce comportement sans affecter ce que l’utilisateur final voit.

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

HTML:

 

CSS (nouveau seulement):

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

3. Pas de défilement n’importe où sauf en mode modal

La solution ci-dessus permet toujours au parent de recevoir des événements de défilement, tant qu’ils ne sont pas interceptés par la fenêtre modale (c’est-à-dire s’ils sont déclenchés par Mousewheel alors que le curseur n’est pas sur le modal). Cela est parfois indésirable et nous pouvons vouloir interdire tout défilement en arrière-plan lorsque le modal est actif. Pour ce faire, nous devons insérer un contexte d’emstackment supplémentaire couvrant toute la fenêtre d’affichage derrière le modal. Nous pouvons le faire en affichant une superposition parfaitement positionnée, qui peut être totalement transparente si nécessaire (mais pas la visibility:hidden ).

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

HTML:

 

CSS (nouveau sur # 2):

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

Directive angular JS

J’ai dû envelopper une directive angular. Ce qui suit est un Mashup des autres réponses ici. testé sur Chrome et Internet Explorer 11.

 var app = angular.module('myApp'); app.directive("preventParentScroll", function () { return { ressortingct: "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; } } } }); 

Usage

 
...

Espère que cela aide la prochaine personne qui vient ici d’une recherche Google.

En variante, pour éviter les problèmes de performances liés au scroll ou à la manipulation des mousewheel , vous pouvez utiliser le code ci-dessous:

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

Exemple de travail: http://jsbin.com/damuwinarata/4

Utilisation des propriétés de défilement d’éléments natifs avec la valeur delta du plug-in mousewheel:

 $elem.on('mousewheel', function (e, delta) { // Ressortingcts 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(); } }); 

Toutes les solutions données dans ce thread ne mentionnent pas un moyen existant – et natif – de résoudre ce problème sans réordonner le DOM et / ou utiliser des astuces empêchant les événements. Mais il y a une bonne raison: cette méthode est propriétaire et disponible uniquement sur la plate-forme Web MS. Citant MSDN :

-ms-scroll-chaining property – spécifie le comportement de défilement qui se produit lorsqu’un utilisateur atteint la limite de défilement lors d’une manipulation. Valeur de propriété:

chained – Valeur initiale. L’élément parent défilable le plus proche commence à défiler lorsque l’utilisateur atteint une limite de défilement lors d’une manipulation. Aucun effet de rebond n’est affiché.

none – Un effet de rebond est affiché lorsque l’utilisateur atteint une limite de défilement lors d’une manipulation.

Certes, cette propriété est prise en charge sur IE10 + / Edge uniquement. Pourtant, voici une citation révélasortingce :

Pour vous donner une idée de la popularité de l’empêchement du chaînage de défilement, selon ma rapide recherche de fichiers http-archive, “-ms-scroll-chaining: none” est utilisé dans 0,4% des 300 premières pages, même si ses fonctionnalités sont limitées. IE / Edge.

Et maintenant, bonne nouvelle à tous! À partir de Chrome 63, nous avons enfin un remède natif pour les plates-formes basées sur Blink – et c’est à la fois Chrome (évidemment) et Android WebView (bientôt).

Citant l’article introductif :

La propriété overscroll-behavior est une nouvelle fonctionnalité CSS qui contrôle le comportement de ce qui se passe lorsque vous faites défiler un conteneur de manière excessive (y compris la page elle-même). Vous pouvez l’utiliser pour annuler le chaînage de défilement, désactiver / personnaliser l’action de rafraîchissement, désactiver les effets de segmentation sur iOS (lorsque Safari implémente le comportement de dérapage), etc.

La propriété prend trois valeurs possibles:

auto – Par défaut. Les parchemins provenant de l’élément peuvent se propager aux éléments ancêtres.

contenir – empêche le chaînage de défilement. Les parchemins ne se propagent pas aux ancêtres, mais les effets locaux au sein du noeud sont affichés. Par exemple, l’effet de surbrillance sur Android ou l’effet de caoutchouc sur iOS qui avertit l’utilisateur lorsqu’il atteint une limite de défilement. Remarque: l’utilisation de overscroll-behavior: contient l’élément html empêche les actions de navigation de défilement excessif.

aucune – identique à contenir, mais elle empêche également les effets de surimpression au sein du nœud lui-même (par exemple, la surimpression Android ou la bande de roulement iOS).

[…] La meilleure partie est que l’utilisation du comportement de dérapage n’affecte pas les performances de la page comme le font les hacks mentionnés dans l’intro!

Voici cette fonctionnalité en action . Et voici le document CSS Module correspondant.

MISE À JOUR: Firefox, depuis la version 59, a rejoint le club et MS Edge devrait implémenter cette fonctionnalité dans la version 18. Voici le caniusage correspondant.

Si quelqu’un cherche encore une solution, le plug-in suivant fait le travail http://mohammadyounes.github.io/jquery-scrollLock/

Il corrige complètement le problème du locking de la molette de la souris dans un conteneur donné, l’empêchant de se propager à l’élément parent.

Il ne change pas la vitesse de défilement des roues, l’expérience utilisateur ne sera pas affectée. et vous obtenez le même comportement quelle que soit la vitesse de défilement vertical de la molette de la souris du système d’exploitation (sous Windows, il peut être défini sur un écran ou une ligne jusqu’à 100 lignes par encoche).

Démo: http://mohammadyounes.github.io/jquery-scrollLock/example/

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

Voici une version 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); 

réponse d’amustill en tant que gestionnaire de KO:

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

Cela fonctionne réellement dans AngularJS. Testé sur Chrome et Firefox.

 .directive('stopScroll', function () { return { ressortingct: '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(); } }); } }; }) 

la méthode ci-dessus n’est pas si naturelle, après quelques recherches sur Google, je trouve une solution plus agréable, sans avoir besoin de jQuery. voir [1] et démo [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; } 

Pour ceux qui utilisent MooTools, voici un code équivalent:

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

Gardez à l’esprit que je devais, comme d’autres, modifier une valeur de quelques px, c’est ce à quoi correspond la hauteur = 2.

La principale différence réside dans le fait que dans MooTools, les informations delta proviennent de event.wheel au lieu d’un paramètre supplémentaire transmis à l’événement.

En outre, j’ai eu des problèmes si je liais ce code à quelque chose (event.target.scrollHeight pour une fonction liée ne correspond pas à this.scrollHeight pour un non-lié)

J’espère que cela aide autant que ce poste m’a aidé;)

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

Nouveau dev web ici. Cela a fonctionné comme un charme pour moi sur IE et 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(); } } } 

Ne laissez pas le nombre de lignes vous tromper, il est assez simple - juste un peu verbeux pour la lisibilité (code auto-documentant ftw à droite?)

Je devrais également mentionner que la langue ici est TypeScript , mais comme toujours, il est simple de la convertir en JS.

Je l’ai extrait de la bibliothèque choisie: 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) 

Cela fonctionne pour moi.

Je déteste la publication de necro mais je cherchais ceci pour MooTools et c’était la première fois que cela se produisait. L’exemple original de MooTools fonctionnerait avec le défilement vers le haut, mais sans faire défiler, j’ai donc décidé d’écrire celui-ci.


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

Usage:

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

Plugin jQuery avec emuler un défilement naturel pour 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

La meilleure solution que je pouvais trouver consistait à écouter l’événement scroll dans la fenêtre et à définir le scrollTop sur le scrollTop précédent si la div enfant était 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

N’utilisez pas de overflow: hidden; sur le body . Il fait automatiquement défiler tout en haut. Il n’y a pas besoin de JavaScript non plus. Utilisez le overflow: auto; :

Structure HTML

 
lengthy content here

Coiffant

 .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 sortingck 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 atsortingbute

     (function() { 'use ssortingct'; angular .module('app') .directive('isolateScrolling', isolateScrolling); function isolateScrolling() { return { ressortingct: '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 atsortingbute 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'); });