web-dev-qa-db-fra.com

Comment empêcher le défilement de page lors du défilement d'un élément DIV?

J'ai passé en revue et testé les différentes fonctions permettant d'empêcher le corps de se déplacer en étant à l'intérieur d'une div et ai combiné une fonction qui devrait fonctionner.

$('.scrollable').mouseenter(function() {
    $('body').bind('mousewheel DOMMouseScroll', function() {
        return false;
    });
    $(this).bind('mousewheel DOMMouseScroll', function() {
        return true;
    });
});
$('.scrollable').mouseleave(function() {
    $('body').bind('mousewheel DOMMouseScroll', function() {
        return true;
    });
});
  • Ceci arrête tout défilement là où je veux que le défilement soit toujours possible à l'intérieur du conteneur
  • Cela ne désactive pas non plus en laissant la souris

Des idées ou de meilleures façons de le faire?

86
Robin Knight

Mise à jour 2: Ma solution est basée sur la désactivation complète du défilement natif du navigateur (lorsque le curseur est dans la DIV), puis sur le défilement manuel de la DIV avec JavaScript (en définissant sa propriété .scrollTop). Une autre approche et une meilleure approche par l’OMI consisterait à ne désactiver que sélectivement le défilement du navigateur afin d’empêcher le défilement de la page, mais pas le défilement DIV. Découvrez la réponse de Rudie ci-dessous qui illustre cette solution. 


Voici:

$( '.scrollable' ).on( 'mousewheel DOMMouseScroll', function ( e ) {
    var e0 = e.originalEvent,
        delta = e0.wheelDelta || -e0.detail;

    this.scrollTop += ( delta < 0 ? 1 : -1 ) * 30;
    e.preventDefault();
});

Démo en direct:https://jsbin.com/howojuq/edit?js,output

Donc, vous définissez manuellement la position de défilement, puis empêchez simplement le comportement par défaut (qui consisterait à faire défiler la page Web DIV ou entière).

Mise à jour 1: Comme Chris l'a noté dans les commentaires ci-dessous, dans les versions plus récentes de jQuery, les informations de delta sont imbriquées dans l'objet .originalEvent, autrement dit, jQuery ne les expose plus dans son objet Event personnalisé. l'objet Event natif à la place.

158
Šime Vidas

Si vous ne vous souciez pas de la compatibilité avec les anciennes versions IE (<8), vous pouvez créer un plugin jQuery personnalisé, puis l'appeler sur l'élément débordant.

Cette solution présente un avantage par rapport à celui proposé par Šime Vidas, car elle n'écrase pas le comportement de défilement - elle le bloque uniquement lorsque cela est approprié.

$.fn.isolatedScroll = function() {
    this.bind('mousewheel DOMMouseScroll', function (e) {
        var delta = e.wheelDelta || (e.originalEvent && e.originalEvent.wheelDelta) || -e.detail,
            bottomOverflow = this.scrollTop + $(this).outerHeight() - this.scrollHeight >= 0,
            topOverflow = this.scrollTop <= 0;

        if ((delta < 0 && bottomOverflow) || (delta > 0 && topOverflow)) {
            e.preventDefault();
        }
    });
    return this;
};

$('.scrollable').isolatedScroll();
28
wojcikstefan

Je pense qu'il est possible d'annuler parfois l'événement mousescroll: http://jsfiddle.net/rudiedirkx/F8qSq/show/

$elem.on('wheel', function(e) {
    var d = e.originalEvent.deltaY,
        dir = d < 0 ? 'up' : 'down',
        stop = (dir == 'up' && this.scrollTop == 0) || 
               (dir == 'down' && this.scrollTop == this.scrollHeight-this.offsetHeight);
    stop && e.preventDefault();
});

Dans le gestionnaire d'événements, vous devez connaître:

  • sens de défilement
    d = e.originalEvent.deltaY, dir = d < 0 ? 'up' : 'down' car un nombre positif signifie défiler vers le bas
  • position de défilement
    scrollTop pour le haut, scrollHeight - scrollTop - offsetHeight pour le bas

Si vous êtes

  • faire défiler vers le haut, et top = 0, ou
  • faire défiler vers le bas, et bottom = 0,

annuler l'événement: e.preventDefault() (et peut-être même e.stopPropagation()).

Je pense qu'il est préférable de ne pas remplacer le comportement de défilement du navigateur. Ne l'annulez que le cas échéant.

Ce n'est probablement pas parfaitement xbrowser, mais cela ne peut pas être très difficile. La double direction de défilement de Mac est peut-être délicate ...

21
Rudie

Dans la solution ci-dessus, il y a une petite erreur concernant Firefox. Dans Firefox, l'événement "DOMMouseScroll" n'a pas de propriété e.detail. Pour obtenir cette propriété, vous devez écrire le message 'e.originalEvent.detail' suivant.

Voici une solution de travail pour Firefox:

$.fn.isolatedScroll = function() {
    this.on('mousewheel DOMMouseScroll', function (e) {
        var delta = e.wheelDelta || (e.originalEvent && e.originalEvent.wheelDelta) || -e.originalEvent.detail,
            bottomOverflow = (this.scrollTop + $(this).outerHeight() - this.scrollHeight) >= 0,
            topOverflow = this.scrollTop <= 0;

        if ((delta < 0 && bottomOverflow) || (delta > 0 && topOverflow)) {
            e.preventDefault();
        }
    });
    return this;
};
3
Vladimir Kuzmin

voici une solution simple sans jQuery qui ne détruit pas le défilement natif du navigateur (c’est-à-dire: aucun défilement artificiel/laid):

var scrollable = document.querySelector('.scrollable');

scrollable.addEventListener('wheel', function(event) {
    var deltaY = event.deltaY;
    var contentHeight = scrollable.scrollHeight;
    var visibleHeight = scrollable.offsetHeight;
    var scrollTop = scrollable.scrollTop;

    if (scrollTop === 0 && deltaY < 0)
        event.preventDefault();
    else if (visibleHeight + scrollTop === contentHeight && deltaY > 0)
        event.preventDefault();
});

Démo en direct: http://jsfiddle.net/ibcaliax/bwmzfmq7/4/

3

voir si cela vous aide:

démo: jsfiddle

$('#notscroll').bind('mousewheel', function() {
     return false
});

modifier: 

essaye ça:

    $("body").delegate("div.scrollable","mouseover mouseout", function(e){
       if(e.type === "mouseover"){
           $('body').bind('mousewheel',function(){
               return false;
           });
       }else if(e.type === "mouseout"){
           $('body').bind('mousewheel',function(){
               return true;
           });
       }
    });
3
Ricardo Binns

Une solution moins astucieuse, à mon avis, consiste à définir le débordement masqué sur le corps lorsque vous passez la souris sur la div à défilement. Cela empêchera le corps de défiler, mais un effet de "saut" indésirable se produira. La solution suivante fonctionne autour de cela:

jQuery(".scrollable")
    .mouseenter(function(e) {
        // get body width now
        var body_width = jQuery("body").width();
        // set overflow hidden on body. this will prevent it scrolling
        jQuery("body").css("overflow", "hidden"); 
        // get new body width. no scrollbar now, so it will be bigger
        var new_body_width = jQuery("body").width();
        // set the difference between new width and old width as padding to prevent jumps                                     
        jQuery("body").css("padding-right", (new_body_width - body_width)+"px");
    })
    .mouseleave(function(e) {
        jQuery("body").css({
            overflow: "auto",
            padding-right: "0px"
        });
    })

Vous pouvez rendre votre code plus intelligent si nécessaire. Par exemple, vous pouvez vérifier si le corps a déjà un remplissage et si oui ajouter le nouveau remplissage à celui-ci. 

3
Alexandru Lighezan

Voici ma solution que j'ai utilisée dans les applications.

J'ai désactivé le débordement du corps et placé l'intégralité du site Web html dans le conteneur div. Les conteneurs de site Web ont débordé et l’utilisateur peut donc faire défiler la page comme prévu.

J'ai ensuite créé un div frère (#Prevent) avec un index z supérieur qui couvre l'ensemble du site. Comme #Prevent a un index z plus élevé, il chevauche le conteneur de site Web. Lorsque #Prevent est visible, la souris ne survole plus les conteneurs du site Web, ce qui rend le défilement impossible.

Vous pouvez bien sûr placer un autre div, tel que votre modal, avec un indice z supérieur à #Prevent dans le balisage. Cela vous permet de créer des fenêtres contextuelles qui ne souffrent pas de problèmes de défilement.

Cette solution est préférable car elle ne masque pas les barres de défilement (effet de saut). Il ne nécessite pas d'auditeurs d'événements et est facile à mettre en œuvre. Cela fonctionne dans tous les navigateurs, bien qu'avec IE7 & 8, vous devez jouer (dépend de votre code spécifique). 

html

<body>
  <div id="YourModal" style="display:none;"></div>
  <div id="Prevent" style="display:none;"></div>
  <div id="WebsiteContainer">
     <div id="Website">
     website goes here...
     </div>
  </div>
</body>

css

body { overflow: hidden; }

#YourModal {
 z-index:200;
 /* modal styles here */
}

#Prevent {
 z-index:100;
 position:absolute;
 left:0px;
 height:100%;
 width:100%;
 background:transparent;
}

#WebsiteContainer {
  z-index:50;
  overflow:auto;
  position: absolute;
  height:100%;
  width:100%;
}
#Website {
  position:relative;
}

jquery/js

function PreventScroll(A) { 
  switch (A) {
    case 'on': $('#Prevent').show(); break;
    case 'off': $('#Prevent').hide(); break;
  }
}

désactiver/activer le défilement

PreventScroll('on'); // prevent scrolling
PreventScroll('off'); // allow scrolling
2
David D

J'avais besoin d'ajouter cet événement à plusieurs éléments pouvant comporter une barre de défilement. Dans les cas où aucune barre de défilement n'était présente, la barre de défilement principale ne fonctionnait pas correctement. J'ai donc apporté une petite modification au code @ Šime comme suit:

$( '.scrollable' ).on( 'mousewheel DOMMouseScroll', function ( e ) {
    if($(this).prop('scrollHeight') > $(this).height())
    {
        var e0 = e.originalEvent, delta = e0.wheelDelta || -e0.detail;

        this.scrollTop += ( delta < 0 ? 1 : -1 ) * 30;
        e.preventDefault();
    }       
});

Désormais, seuls les éléments dotés d'une barre de défilement empêchent le défilement principal de commencer arrêté.

1
Ricky

Version javascript pure de la réponse de Vidas, el$ est le noeud DOM de l'avion que vous faites défiler.

function onlyScrollElement(event, el$) {
    var delta = event.wheelDelta || -event.detail;
    el$.scrollTop += (delta < 0 ? 1 : -1) * 10;
    event.preventDefault();
}

Assurez-vous de ne pas joindre le même plusieurs fois! Voici un exemple,

var ul$ = document.getElementById('yo-list');
// IE9, Chrome, Safari, Opera
ul$.removeEventListener('mousewheel', onlyScrollElement);
ul$.addEventListener('mousewheel', onlyScrollElement);
// Firefox
ul$.removeEventListener('DOMMouseScroll', onlyScrollElement);
ul$.addEventListener('DOMMouseScroll', onlyScrollElement);

Mot d'avertissement , la fonction doit être constante si vous réinitialisez la fonction à chaque fois avant de la joindre, c'est-à-dire. var func = function (...) la removeEventListener ne fonctionnera pas.

0
srcspider

Vous pouvez le faire sans JavaScript. Vous pouvez définir le style sur les deux divs avec position: fixed et overflow-y: auto. Vous devrez peut-être rendre l’un supérieur à l’autre en définissant son z-index (s’ils se chevauchent).

Voici un exemple de base sur CodePen .

0
Carl Smith

Voici le plugin qui est utile pour empêcher le défilement du parent lors du défilement d’une div spécifique et qui possède de nombreuses options pour jouer.

Vérifiez le ici: 

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

Utilisation

Verrouillage du défilement via JavaScript:

$('#target').scrollLock();

Verrouillage du défilement via le marquage:

    <!-- HTML -->
<div data-scrollLock
     data-strict='true'
     data-selector='.child'
     data-animation='{"top":"top locked","bottom":"bottom locked"}'
     data-keyboard='{"tabindex":0}'
     data-unblock='.inner'>
     ...
</div>

<!-- JavaScript -->
<script type="text/javascript">
  $('[data-scrollLock]').scrollLock()
</script>

Voir Démo

0
MR_AMDEV