Empêcher le défilement de l'élément parent lorsque la position de défilement de l'élément intérieur atteint le haut / le bas?

J'ai une petite "boîte à outils flottante" - une div avec position:fixed; overflow:auto. Fonctionne très bien.

Mais lorsque vous faites défiler l'intérieur de cette boîte (avec la molette de la souris) et que vous atteignez le bas ou le haut, l'élément parent "reprend" la "requête de défilement": le document derrière la boîte à outils défile.
- Ce qui est ennuyeux, et pas ce que l'utilisateur "demandé".

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

Il entre dans la fonction, mais pourtant, la propagation se produit de toute façon (le document défile)
- Il est étonnamment difficile de rechercher ce sujet sur SO (et Google), donc je dois demander:
Comment empêcher la propagation / bouillonnement de l'événement scroll ?

Modifier:
Solution de travail grâce à amustill (et Brandon Aaron pour le MouseWheel-plugin here:
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();
        }
    }
});
177
demandé sur Pandaiolo 2011-04-27 14:13:28

29 réponses

C'est possible avec L'utilisation du plugin Mousewheel de Brandon Aaron.

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

37
répondu amustill 2016-03-01 22:23:40

J'ajoute cette réponse pour être complète car la réponse acceptée par @ amustill ne résout pas correctement le problème dans Internet Explorer . Veuillez consulter les commentaires dans mon message original pour plus de détails. En outre, cette solution ne nécessite aucun plugin-seulement jQuery.

En substance, le code fonctionne en gérant l'événement mousewheel. Chaque événement contient un wheelDelta égal au nombre de px qu'il va déplacer la zone déroulante pour. 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 {[9] } comme événement, et remplit originalEvent.detail, dont +/- est inversé par rapport à ce qui est décrit ci-dessus. Il renvoie généralement des intervalles de 3, tandis que d'autres navigateurs renvoient le défilement à des intervalles de 120 (au moins sur ma machine). Pour corriger, nous le détectons simplement et multiplions par -40 pour normaliser.

@la réponse d'amustill {[38] } Fonctionne en annulant le événement si la zone de défilement de <div> est déjà en position maximale supérieure ou inférieure. Cependant, Internet Explorer ne tient pas compte de l'événement annulé dans les situations où le delta est plus grand que l'espace de défilement restant.

En d'autres termes, si vous avez un 200px haut <div> contenant 500px de contenu défilant, et le courant scrollTop est 400, un mousewheel événement qui indique au navigateur pour faire défiler 120px d'autres se traduira par la <div> et le <body> défilement, parce que 400 + 120 > 500.

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

Le code jQuery requis 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();
    }
});

En substance, 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 {[20] } du <div> à la valeur maximale ou minimale, en fonction de la direction que l'événement mousewheel demandait.

Parce que l'événement est annulé entièrement dans les deux cas, il ne se propage jamais au body, et résout donc le problème dans IE, ainsi que tous les autres navigateurs.

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

165
répondu Troy Alford 2017-05-23 12:10:01

Je sais c'est une vieille question, mais puisque c'est l'un des meilleurs résultats dans google... J'ai dû en quelque sorte annuler le bouillonnement 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);
}
19
répondu Nebril 2012-03-20 11:58:05

Modifier: Exemple CodePen

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

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

Puis l'a ajouté à l'élément défilant (le menu déroulant ul):

<div class="dropdown">
  <button type="button" class="btn dropdown-toggle">Rename <span class="caret"></span></button>
  <ul class="dropdown-menu" isolate-scrolling>
    <li ng-repeat="s in savedSettings | objectToArray | orderBy:'name' track by s.name">
      <a ng-click="renameSettings(s.name)">{{s.name}}</a>
    </li>
  </ul>
</div>

Testé sur Chrome et Firefox. Défilement lisse de Chrome défaites ce hack quand un grand mouvement de la roue de la souris est faite près (mais pas à) le haut ou le bas de la région de défilement.

17
répondu dOxxx 2018-05-20 14:19:22

Il y a des tonnes de questions comme celle-ci, avec beaucoup de réponses, mais je n'ai pas pu trouver une solution satisfaisante qui n'impliquait pas d'événements, de scripts, de plugins, etc. Je voulais le garder droit en HTML et CSS. J'ai finalement trouvé une solution qui a fonctionné, bien qu'elle implique la restructuration du balisage pour briser la chaîne d'événements.


1. Problème de base

L'entrée de défilement (c'est-à-dire: mousewheel) appliquée à l'élément modal débordera dans un élément ancêtre et faites-le défiler dans la même direction, 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:

<div id="parent">
  <div id="modal">
    This text is pretty long here.  Hope fully, we will get some scroll bars.
  </div>
</div>

CSS:

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

2. Pas de défilement parent sur le défilement modal

La raison pour laquelle l'ancêtre finit par défiler est que l'événement de défilement bulles et un élément de la chaîne est capable de le gérer. Un moyen de les arrêter que est de s'assurer qu'aucun des éléments de la chaîne de savoir comment gérer le défilement. En termes de notre exemple, nous pouvons refactoriser l'arbre pour déplacer le modal hors de l'élément parent. Pour des raisons obscures, il ne suffit pas de garder le parent et les frères et sœurs Dom modaux; le parent doit être enveloppé par un autre élément qui établit un nouveau contexte d'empilement. Un emballage absolument positionné autour du parent peut faire l'affaire.

Le résultat que nous obtenons est que tant que le modal reçoit l' faites défiler l'événement, l'événement ne fera pas de bulles à l'élément "parent".

Il devrait généralement être possible de redessiner L'arborescence DOM pour prendre en charge ce comportement sans affecter ce que l'utilisateur final voit.

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

HTML:

<div id="context">
  <div id="parent">
  </div>
</div>
<div id="modal">
  This text is pretty long here.  Hope fully, we will get some scroll bars.
</div>

CSS (Nouveau seulement):

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

3. Aucun défilement n'importe où sauf en modal alors qu'il est en place

La solution ci-dessus permet toujours au parent de recevoir des événements de défilement, tant qu'ils le sont non intercepté par la fenêtre modale (c'est-à-dire si déclenché par mousewheel alors que le curseur n'est pas sur le modal). Ceci est parfois indésirable et nous pouvons vouloir interdire tout défilement en arrière-plan pendant que le modal est en place. Pour ce faire, nous devons insérer un contexte d'empilement supplémentaire qui couvre toute la fenêtre derrière le modal. Nous pouvons le faire en affichant une superposition absolument positionnée, qui peut être entièrement transparente si nécessaire (mais pas visibility:hidden).

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

HTML:

<div id="context">
  <div id="parent">
  </div>
</div>
<div id="overlay">  
</div>
<div id="modal">
  This text is pretty long here.  Hope fully, we will get some scroll bars.
</div>

CSS (nouveau au-dessus de #2):

#overlay {
  background-color: transparent;
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
}
9
répondu Daniel S. 2015-12-24 10:12:17

Directive JS angulaire

J'ai dû envelopper une directive angulaire. Voici un Mashup des autres réponses ici. testé sur Chrome et 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;
            }
        }
    }
});

Utilisation

<div prevent-parent-scroll>
    ...
</div>

Espère que cela aide la prochaine personne qui arrive ici à partir d'une recherche Google.

9
répondu Jossef Harush 2016-12-15 11:37:55

En tant que variante, pour éviter les problèmes de performance avec la gestion scroll ou mousewheel, Vous pouvez utiliser le code comme ci-dessous:

Css:

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

Html:

<div class="scrollable">
...A bunch of items to make the div scroll...
</div>
...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 travaux: http://jsbin.com/damuwinarata/4

8
répondu Bohdan Lyzanets 2014-09-10 14:33:52

Utilisation des propriétés de défilement des éléments natifs avec la valeur delta du plugin 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();
    }
});
7
répondu Pete B 2013-06-19 12:55:34

Toutes les solutions données dans ce thread ne mentionnent pas un moyen existant - et natif-de résoudre ce problème sans réorganiser DOM et / ou utiliser des astuces de prévention des événements. Mais il y a une bonne raison: cette méthode est propriétaire-et disponible uniquement sur MS web platform. Citant MSDN:

-mme de défilement de la chaîne propriété spécifie le comportement de défilement qui se produit lorsqu'un utilisateur atteint la limite de défilement lors d'une manipulation. Valeurs de propriété:

Enchaînés - valeur Initiale. L'élément parent défilant le plus proche commence à défiler lorsque l'utilisateur atteint une limite de défilement lors d'une manipulation. Pas d'effet de rebond est indiqué.

None - un effet de rebond est affiché lorsque l'utilisateur atteint une limite de défilement lors d'une manipulation.

Accordée, cette propriété est prise en charge sur IE10+/Edge uniquement. Pourtant, voici un Citation de dire:

Pour vous donner une idée de la popularité de la prévention du défilement le chaînage peut être, selon ma recherche rapide http-archive " - ms-scroll-chaining: aucun" est utilisé dans 0.4% de top 300K pages en dépit d'être limité dans fonctionnalité et uniquement pris en charge sur IE / Edge.

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

Citant l'introduction de l'article:

Le overscroll-comportement la propriété est une nouvelle fonctionnalité CSS qui contrôle le comportement de ce qui se passe lorsque vous faites défiler un conteneur (y compris la page elle-même). Vous pouvez l'utiliser pour annuler le chaînage de défilement, désactiver / personnaliser l'action pull-to-refresh, désactiver rubberbanding effets sur iOS (lorsque Safari implémente overscroll-behavior), et plus encore.[...]

La propriété prend trois valeurs possibles:

Auto Par Défaut. Les parchemins qui proviennent de l'élément peuvent propager à ancêtre des éléments.

Contain - empêche le chaînage par défilement. Les parchemins ne le font pas propager aux ancêtres, mais les effets locaux dans le nœud sont affichés. Par exemple, l'effet overscroll glow sur Android ou le effet rubberbanding sur iOS qui avertit l'utilisateur quand ils ont frappé un défilement de la frontière. Remarque: utilisation de overscroll-behavior: contain sur le html élément empêche les actions de navigation overscroll.

Aucun - même comme le contenir, mais il a également empêche les effets de surenchère dans le nœud lui-même (par exemple, Android overscroll glow ou iOS rubberbanding).

[...] la meilleure partie est que l'utilisation du comportement overscroll ne nuit pas affecter les performances de la page comme les hacks mentionnés dans l'intro!

Voici ce fonctionnalité en action. Et voici correspondant document du Module CSS.

Mise à jour: Firefox, depuis la version 59, a rejoint le club, et MS Edge est devrait implémenter cette fonctionnalité dans la version 18. Voici le correspondant caniusage.

7
répondu raina77ow 2018-06-19 15:33:23

Dans le cas où quelqu'un est toujours à la recherche d'une solution pour cela, le plugin suivant fait le travail http://mohammadyounes.github.io/jquery-scrollLock/

Il résout entièrement le problème du verrouillage du défilement de la molette de la souris à l'intérieur d'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 verticale de la molette de la souris du système D'exploitation (sous Windows il peut être réglé 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

6
répondu MK. 2014-10-24 12:47:17

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);
6
répondu silverwind 2016-03-19 00:01:13

La réponse D'Amustill en tant que gestionnaire de Knock-Out:

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();
                }
            }
        });
    }
};
4
répondu mhu 2013-03-18 14:45:23

Cela fonctionne réellement dans AngularJS. Testé sur Chrome et 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();
                }
            });
        }
    };
})
4
répondu Remot 2015-06-24 19:32:59

La méthode ci-dessus n'est pas si naturelle, après quelques recherches sur Google , je trouve une solution plus agréable, et pas 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;
  }

[1] https://dimakuzmich.wordpress.com/2013/07/16/prevent-scrolling-of-parent-element-with-javascript/

[2] http://jsfiddle.net/dima_k/5mPkB/1/

3
répondu jinwei 2015-03-24 09:44:20

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, comme d'autres, j'ai dû modifier une valeur par quelques px, c'est à quoi sert la hauteur -= 2.

Fondamentalement, la principale différence est que dans MooTools, les informations delta proviennent de l'événement.roue 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 à n'importe quoi (événement.cible.scrollHeight pour une fonction liée ne correspond pas à cela.scrollHeight pour un non lié)

Espère que cela aide quelqu'un autant que ce post m'a aidé ;)

1
répondu Clive Galway 2011-12-11 12:31:50

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;
    });
}
1
répondu psycho brm 2012-09-14 14:28:28

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, c'est assez simple - juste un peu verbeux pour la lisibilité (Code Auto documentant ftw Non?)

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

1
répondu Vivek Maharajh 2015-03-25 16:52:25

J'ai lu ceci 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.

1
répondu Drew LeSueur 2017-02-27 10:42:55

Je déteste l'affichage de necro mais je cherchais ceci pour MooTools et c'était le premier qui est venu. L'exemple original de MooTools fonctionnerait avec le défilement vers le haut, mais pas vers le bas, alors j'ai 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;
};

Utilisation:

(function)($){
    window.addEvent('domready', function(){
        $$('.scrollable').addEvents({
             'mousewheel': stopScroll,
             'DOMMouseScroll': stopScroll
        });
    });
})(document.id);
0
répondu fyrye 2013-04-01 21:04:08

Plugin JQuery avec émuler le 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

0
répondu B.Asselin 2014-02-02 10:48:41

La meilleure solution que j'ai pu trouver était d'écouter l'événement scroll sur la fenêtre et de définir le scrollTop sur le scrollTop précédent si le div enfant était visible.

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

Il y a un scintillement en arrière-plan de la div enfant si vous déclenchez beaucoup d'événements de défilement, donc cela pourrait être modifié, mais il est à peine remarqué et c'était suffisant pour mon cas d'utilisation.

0
répondu GijsjanB 2014-05-08 09:32:37

J'ai une situation similaire, et voici comment je l'ai résolu:
Tous mes éléments scrollables obtiennent la classe 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 () veille à ne pas faire défiler la zone de défilement parent de la zone de défilement enfant.

Voici une implémentation de vanilla js: http://jsbin.com/lugim/2/edit?js,sortie

0
répondu user3259967 2014-08-21 15:06:49

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

Structure HTML

<div class="overlay">
    <div class="overlay-content"></div>
</div>

<div class="background-content">
    lengthy content here
</div>

Style

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

Jouer avec la démo ici.

0
répondu Luxiyalu 2015-06-24 11:11:07

Consultez le code de Leland Kwong.

L'idée de base est de lier l'événement wheeling à l'élément enfant, puis d'utiliser la propriété JavaScript native scrollHeight et la propriété jquery outerHeight de l'élément enfant pour détecter la fin du défilement, sur laquelle return false à l'événement wheeling pour empêcher tout défilement.

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;
  }
});
0
répondu Vic 2015-11-28 13:44:21

Il y a aussi une astuce amusante pour verrouiller scrollTop du parent lorsque la souris survole un élément défilant. De cette façon, vous n'avez pas à implémenter votre propre défilement de roue.

Voici un exemple pour empêcher le défilement du document, mais il peut être ajusté pour n'importe quel élément.

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');
});
0
répondu Sebastian Nowak 2016-02-15 16:34:15

M. K. offert un super plugin dans sa réponse. Le Plugin peut être trouvé ici. Cependant, pour l'achèvement, j'ai pensé que ce serait une bonne idée de le mettre ensemble dans une réponse pour AngularJS.

  1. Commencez par injecter le bower ou le npm (selon la préférence)

    bower install jquery-scrollLock --save
    npm install jquery-scroll-lock --save
    
  2. Ajouter la directive suivante. Je choisis de l'ajouter en tant qu'attribut

    (function() {
       'use strict';
    
        angular
           .module('app')
           .directive('isolateScrolling', isolateScrolling);
    
           function isolateScrolling() {
               return {
                   restrict: 'A',
                   link: function(sc, elem, attrs) {
                      $('.scroll-container').scrollLock();
                   }
               }
           }
    })();
    
  3. Et la pièce importante que le plugin ne parvient pas à documenter dans leur site web est la structure HTML qu'il doit suivre.

    <div class="scroll-container locked">
        <div class="scrollable" isolate-scrolling>
             ... whatever ...
        </div>
    </div>
    

L'attribut {[3] } doit contenir la classe scrollable et tout doit être à l'intérieur de la classe scroll-container ou de la classe que vous choisissez et la classe locked doit être en cascade.

0
répondu LOTUSMS 2017-05-23 12:26:13

Il convient de mentionner qu'avec les frameworks modernes tels que reactJS, AngularJS, VueJS, etc., il existe des solutions faciles à ce problème, lorsqu'il s'agit d'éléments à position fixe. Des exemples sont des panneaux latéraux ou des éléments superposés.

La technique est appelée un "portail" , ce qui signifie que l'un des composants utilisés dans l'application, sans avoir besoin de l'extraire d'où vous l'utilisez, va monter ses enfants au bas de l'élément du corps, en dehors du parent que vous essayez de évitez le défilement.

Notez que cela n'évitera pas de faire défiler l'élément body lui-même. Vous pouvez combiner cette technique et le montage de votre application dans un div défilement pour atteindre le résultat attendu.

Exemple d'implémentation du portail dans l'interface utilisateur matérielle de React: https://material-ui-next.com/api/portal/

0
répondu Pandaiolo 2018-05-14 10:14:53

Solution Simple avec l'événement mouseweel:

$('.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();
    }
});
-1
répondu egor.xyz 2015-11-05 14:04:02

Vous pouvez l'essayer de cette façon:

$('#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');
});
-2
répondu Korplex 2012-10-11 07:55:07