Faire défiler la div enfant fait défiler la fenêtre, comment puis-je arrêter cela?

Réponses:

68

Vous pouvez désactiver le défilement de la page entière en faisant quelque chose comme ceci:

<div onmouseover="document.body.style.overflow='hidden';" onmouseout="document.body.style.overflow='auto';"></div>
Teemu
la source
33
Bien, mais cela fait disparaître la barre de défilement du navigateur chaque fois que vous survolez le div.
cstack
@cstack vous avez raison, c'est pourquoi la réponse d'OP (Jeevan) est meilleure.
Imran Bughio le
2
De nombreuses roues de défilement du navigateur disparaissent et réapparaissent en fonction du déplacement de la souris par l'utilisateur, de sorte que le commentaire ci-dessus n'est plus un problème.
Keith Holliday
Cette solution ne fonctionne pas lorsque la page est intégrée dans un iframe.
Gautam Krishnan
5
pas une solution. définir le débordement sur hidden masque la barre de défilement qui reformate soudainement tout le contenu de l'élément externe. aussi, cela suppose que l'élément externe est body mais il pourrait s'agir d'un autre div de défilement.
robisrob
43

J'ai trouvé la solution.

http://jsbin.com/itajok

C'est ce dont j'avais besoin.

Et c'est le code.

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

Utilise un plug-in jQuery.


Mise à jour en raison d'un avis d'abandon

De jquery-mousewheel :

L'ancien comportement consistant à ajouter trois arguments ( delta, deltaXet deltaY) au gestionnaire d'événements est désormais obsolète et sera supprimé dans les versions ultérieures.

Ensuite, event.deltaYdoit maintenant être utilisé:

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

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

Démo

Jeevan
la source
6
Votre lien utilise github pour les js et les pauses parce que le type de contenu bla bla ici celui-ci fonctionne: jsbin.com/itajok/207
Michael J. Calkins
Les deux jsbins ne semblent pas fonctionner dans l'émulateur d'appareil Chrome et sur l'iPhone. Est-ce que quelqu'un sait si cela fonctionne toujours?
Dirk Boer du
2
Votre «solution» ne fonctionne que pour les événements de défilement de la souris, pas de page haut / bas ou les touches fléchées.
Scott
Pourquoi "off" est-il nécessaire ici?
Brad Johnson
16

La solution choisie est une œuvre d'art. Je pensais que c'était digne d'un plugin ...

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

Cela a été un inconvénient permanent pour moi et cette solution est si propre par rapport aux autres hacks que j'ai vus. Curieux de savoir comment cela fonctionne et à quel point il serait largement soutenu, mais bravo à Jeevan et à celui qui a initialement proposé cela. BTW - L'éditeur de réponses stackoverflow en a besoin!

METTRE À JOUR

Je pense que c'est mieux en ce sens qu'il n'essaye pas du tout de manipuler le DOM, cela empêche seulement le bouillonnement conditionnel ...

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

Fonctionne très bien en chrome et beaucoup plus simple que les autres solutions ... faites-moi savoir comment cela se passe ailleurs ...

VIOLON

robisrob
la source
3
l'ajout d'un événement DOMMouseScroll le fait fonctionner avec Firefox → jsfiddle.net/chodorowicz/egqy7mbz/1
chodorowicz
Je suis surpris que ce ne soit pas écrit dans jquery. Je pensais que l'un des principaux points était de ne pas avoir à se soucier du navigateur que vous utilisez.
robisrob
en fait, il semble que les deux sont obsolètes ... wheelevent
robisrob
Cela se traduit par un défilement nettement plus saccadé dans Chrome sur mon Macbook. La solution acceptée n'a pas ce problème.
danvk
décevant. il existe tellement de versions différentes de cela. semble être un aspect important de l'interface utilisateur, et il est vraiment surprenant que ce soit le comportement par défaut quand il est si peu intuitif. De toute évidence, le navigateur est équipé pour savoir que le div de défilement interne est à sa limite pour pouvoir appliquer l'événement à la fenêtre à la place, alors pourquoi n'y a-t-il pas de poignées disponibles pour l'interroger?
robisrob
8

Vous pouvez utiliser un événement mouseover sur le div pour désactiver la barre de défilement du corps, puis un événement mouseout pour l'activer à nouveau?

Par exemple, le HTML

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

Et puis le javascript comme ceci:

var body = document.getElementsByTagName('body')[0];
function disableBodyScroll() {
    body.style.overflowY = 'hidden';
}
function enableBodyScroll() {
    body.style.overflowY = 'auto';
}
Joe92
la source
12
Vous pouvez également utiliser à la document.bodyplace.
Ry-
8

Voici une façon multi-navigateurs de le faire sur l'axe Y, cela fonctionne sur ordinateur de bureau et mobile. Testé sur OSX et iOS.

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

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

scrollArea.addEventListener("touchmove", function(event) {
    var scrollTop = this.scrollTop;
    var maxScroll = this.scrollHeight - this.offsetHeight;
    var currentClientY = event.touches[0].clientY;
    var deltaY = this.previousClientY - currentClientY;
    if ( (scrollTop >= maxScroll && deltaY > 0) || (scrollTop === 0 && deltaY < 0) ) {
        event.preventDefault();
    }
    this.previousClientY = currentClientY;
}, {passive:false});
Patrick Matte
la source
Cela fonctionne à merveille! Wow, exactement ce que j'ai cherché. Il a certainement besoin de plus de votes positifs.
delato468
Cette solution fonctionne plutôt bien mais nécessite un petit ajustement: besoin de changer le scrollTop === maxScroll par scrollTop >= maxScroll. Cela semble bizarre sur 1 cas, c'était nécessaire
tchiot.ludo
7

J'ai écrit pour résoudre ce problème

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

  div.addEventListener('mousewheel', function(e) {
    if (div.clientHeight + div.scrollTop + e.deltaY >= div.scrollHeight) {
      e.preventDefault();
      div.scrollTop = div.scrollHeight;
    } else if (div.scrollTop + e.deltaY <= 0) {
      e.preventDefault();
      div.scrollTop = 0;
    }
  }, false);
Alex Golovin
la source
L'une des rares solutions ici qui fonctionne sans que les éléments de la page ne sautent de manière gênante en raison du dépassement caché.
Sceptique
+1 c'est une solution solide. si vous attachez ceci à un conteneur à défilement, vous souhaiterez que div dans cet exemple soit e.currentTarget.
Matt
solution très propre. Merci
Thinh Bui
A fonctionné dans Chrome 61.0.3163.100 et Safari 11.0, mais pas dans FireFox 56.0 (Mac OS El Capitan)
BigJ
C'est la meilleure solution native. Cependant, falsen'a pas besoin d'être spécifié addEventListenercar c'est la valeur par défaut. De plus, les comparaisons doivent être de simples inégalités ( >et <), pas >=ou <=.
Questions Quolonel du
6

Si je comprends bien votre question, vous voulez empêcher le défilement du contenu principal lorsque la souris est sur un div (disons une barre latérale). Pour cela, la barre latérale peut ne pas être un enfant du conteneur de défilement du contenu principal (qui était la fenêtre du navigateur), pour empêcher l'événement de défilement de remonter jusqu'à son parent.

Cela nécessite éventuellement des modifications de balisage de la manière suivante:

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

Voyez que cela fonctionne dans cet exemple de violon et comparez cela avec cet exemple de violon qui a un comportement de sortie de souris légèrement différent de la barre latérale.

Voir aussi faire défiler un seul div particulier avec la barre de défilement principale du navigateur .

NGLN
la source
Dans ce cas, la barre latérale se trouve à l'extérieur de la division d'emballage. Donc, je ne pense pas que le parchemin va bouillonner vers le parent
Jeevan
Par «ce cas», vous entendez votre cas? Sinon: vous avez tout à fait raison. Si tel est le cas: si votre page défile à la fin du défilement div, le message de défilement apparaîtra au parent div, donc je pense que votre page et votre div ne sont pas des frères, mais un parent et un enfant.
NGLN
Oui, Page est le parent de ma div, donc en arrivant à la fin, il commence à faire défiler la page, ce que je ne veux pas.
Jeevan
Si vous ne pouvez pas faire de votre div un frère du contenu principal, ce problème ne peut pas être résolu avec HTML + CSS uniquement. C'est le comportement par défaut.
NGLN
1
Je pense que c'est la solution la plus efficace de toutes les réponses fournies; avec un bon balisage propre à la base d'une page, beaucoup moins de hacks CSS et JS sont nécessaires pour obtenir le comportement souhaité.
6

Comme répondu ici , la plupart des navigateurs modernes prennent désormais en charge la overscroll-behavior: none;propriété CSS, qui empêche le chaînage de défilement. Et c'est tout, juste une ligne!

Andriy Stolyar
la source
2
Cela devrait être la bonne réponse en 2020. Pour mon cas d'utilisation, la meilleure solution était overscroll-behavior: contain;.
Jetée le
3

cela désactive le défilement dans la fenêtre si vous entrez l'élément de sélection. fonctionne comme des charmes.

elements = $(".selector");

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

elements.on('mouseleave', function() {
    $(window).off("scroll.prevent");
});
ceed
la source
C'est en effet une solution de travail. J'en avais besoin en pur js, alors je l'ai réécrit. Si quelqu'un en a besoin, c'est ici . Pour tous ceux qui recherchent toujours la solution, vous devriez consulter ma réponse ci-dessous .
Andriy Stolyar
2

Vous pouvez désactiver le défilement de la page entière en faisant quelque chose comme ça mais affichez la barre de défilement!

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

Sur la base de la réponse de ceed, voici une version qui permet l'imbrication d'éléments protégés par défilement. Seul l'élément sur lequel se trouve la souris défilera et il défilera assez facilement. Cette version est également réentrante. Il peut être utilisé plusieurs fois sur le même élément et supprimera et réinstallera correctement les gestionnaires.

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

Un moyen simple d’utiliser consiste à ajouter une classe, par exemple scroll-guard, à tous les éléments de la page sur lesquels vous autorisez le défilement. Ensuite, utilisez $('.scroll-guard').scrollGuard()pour les garder.

d'Artagnan Evergreen Barbosa
la source
0

Si vous appliquez un overflow: hidden style, il devrait disparaître

edit: en fait j'ai mal lu votre question, cela ne fera que masquer la barre de défilement mais je ne pense pas que ce soit ce que vous recherchez.

JavaKungFu
la source
1
J'ai besoin à la fois de la barre de défilement. Je parle du comportement. Lorsque je fais défiler à l'aide de la boule de commande de ma souris en atteignant la fin du défilement du div, il ne doit pas faire défiler toute la page.
Jeevan
0

Je n'ai pas réussi à faire fonctionner l'une des réponses dans Chrome et Firefox, j'ai donc proposé cette fusion:

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

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