Come impedire lo scorrimento della pagina durante lo scorrimento di un elemento DIV?


94

Ho esaminato e testato le varie funzioni per prevenire la capacità del corpo di scorrere mentre si è all'interno di un div e ho combinato una funzione che dovrebbe funzionare.

$('.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;
    });
});
  • Questo interrompe tutto lo scorrimento dove voglio che lo scorrimento sia ancora possibile all'interno del contenitore
  • Anche questo non si disattiva all'uscita del mouse

Qualche idea o modi migliori per farlo?


hai impostato il corpo come falso di ritorno dalla rotellina del mouse, forse questo è il problema, suppongo che il tuo contenitore sia all'interno del corpo giusto
Ricardo Binns,

@ric_bfa si ma come risolverlo
RIK

invece body, imposta la tua classe / id container
Ricardo Binns,

Forse dovrei chiarire. Quando il mouse è all'interno dell'elemento ".scrollable", la capacità di scorrimento del corpo dovrebbe essere disattivata
RIK

4
Non c'è un modo per farlo con tutti i CSS e senza JavaScript?
Chharvey

Risposte:


165

Aggiornamento 2: la mia soluzione si basa sulla disabilitazione dello scorrimento nativo del browser (quando il cursore si trova all'interno del DIV) e quindi sullo scorrimento manuale del DIV con JavaScript (impostando la sua .scrollTopproprietà). Un'alternativa e un approccio migliore IMO sarebbe disabilitare solo selettivamente lo scorrimento del browser per impedire lo scorrimento della pagina, ma non lo scorrimento DIV. Controlla la risposta di Rudie di seguito che dimostra questa soluzione.


Ecco qui:

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

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

Demo dal vivo: https://jsbin.com/howojuq/edit?js,output

Quindi si imposta manualmente la posizione di scorrimento e quindi si impedisce il comportamento predefinito (che sarebbe lo scorrimento del DIV o dell'intera pagina Web).

Aggiornamento 1: come ha notato Chris nei commenti seguenti, nelle versioni più recenti di jQuery, le informazioni delta sono nidificate all'interno .originalEventdell'oggetto, ovvero jQuery non le espone più nel suo oggetto Event personalizzato e dobbiamo invece recuperarle dall'oggetto Event nativo .


2
@RobinKnight No, non sembra così. Ecco la demo funzionante: jsfiddle.net/XNwbt/1 ed ecco la demo con quelle righe rimosse: jsfiddle.net/XNwbt/2
Šime Vidas

1
Lo scorrimento manuale è all'indietro su Firefox 10, almeno su Linux e Mac. Sembra funzionare correttamente se lo fai -e.detail, testato in Firefox (Mac, Linux), Safari (Mac) e Chromium (Linux).
Anomie

1
@Anomie Questo è corretto. Il segno di Mozilla .detailnon corrisponde al segno di .wheelData(che è quello che hanno Chrome / IE), quindi dobbiamo invertirlo manualmente. Grazie per aver corretto la mia risposta.
Šime Vidas

8
Ho usato questo, grazie! Avevo bisogno di aggiungere questo tho:if( e.originalEvent ) e = e.originalEvent;
Nikso

2
@ ŠimeVidas Ho apportato una modifica a questo dopo averlo implementato nel mio software. Non critico ma possibilmente aggiungere un controllo di integrità per la proprietà scroll per impedire lo scorrimento del deadlock quando l'elemento non ha scroll. if ( $(this)[0].scrollHeight !== $(this).outerHeight() ) { //yourcode }verrà eseguito solo quando è presente una barra di scorrimento.
user0000001

30

Se non ti interessa la compatibilità con le versioni precedenti di IE (<8), puoi creare un plug-in jQuery personalizzato e quindi chiamarlo sull'elemento in overflow.

Questa soluzione ha un vantaggio rispetto a quella proposta da Šime Vidas, in quanto non sovrascrive il comportamento di scorrimento, ma lo blocca solo quando appropriato.

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

2
Grazie! Penso che sia più appropriato non sovrascrivere il comportamento predefinito. Per il supporto cross-browser può essere utilizzato il plugin jQuery Mouse Wheel .
Meettya

Sfortunatamente questo sembra glitch in Chrome 52 (OSX 10.10). Funziona perfettamente in Safari, però.
gelido

1
Grazie! Le altre soluzioni hanno reso lo scrolling super lento e
buggato

@wojcikstefan, ricevo questo avviso in Chrome:[Violation] Added non-passive event listener to a scroll-blocking 'mousewheel' event. Consider marking event handler as 'passive' to make the page more responsive.
Syed

21

Penso che a volte sia possibile annullare l'evento 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();
});

All'interno del gestore eventi, dovrai sapere:

  • direzione di scorrimento
    d = e.originalEvent.deltaY, dir = d < 0 ? 'up' : 'down' perché un numero positivo significa scorrere verso il basso
  • posizione di scorrimento
    scrollTopin alto, scrollHeight - scrollTop - offsetHeightin basso

Se tu sei

  • scorrendo verso l'alto, e top = 0, o
  • scorrendo verso il basso e bottom = 0,

annullare l'evento: e.preventDefault()(e forse anche e.stopPropagation()).

Penso che sia meglio non sovrascrivere il comportamento di scorrimento del browser. Annullalo solo quando applicabile.

È probabile che non sia perfettamente xbrowser, ma non può essere molto difficile. Forse la doppia direzione di scorrimento del Mac è complicata però ...


2
come implemento la stessa funzionalità per i dispositivi (tablet / telefoni) immagino che touchmove sia l'evento vincolante
ViVekH

6

Usa sotto la proprietà CSS overscroll-behavior: contain;per l'elemento figlio


Tutte le altre soluzioni sono eccessive, rispetto a questa regola CSS nativa. Ben fatto.
TechWisdom,

3

vedi se questo ti aiuta:

demo: jsfiddle

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

modificare:

prova questo:

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

I tuoi elementi sono separati, mentre uno dei miei è contenuto nell'altro.
RIK

3

Una soluzione meno hacky, a mio parere, è impostare l'overflow nascosto sul corpo quando si passa il mouse sul div scorrevole. Ciò impedirà al corpo di scorrere, ma si verificherà un effetto di "salto" indesiderato. La seguente soluzione funziona intorno a questo:

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

Potresti rendere il tuo codice più intelligente se necessario. Ad esempio, potresti verificare se il corpo ha già un'imbottitura e, in caso affermativo, aggiungere la nuova imbottitura a quella.


3

Nella soluzione sopra c'è un piccolo errore riguardo a Firefox. In Firefox l'evento "DOMMouseScroll" non ha la proprietà e.detail, per ottenere questa proprietà è necessario scrivere il seguente 'e.originalEvent.detail'.

Ecco una soluzione funzionante per 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;
};

Funziona abbastanza bene tranne quando ti schianti nella parte superiore o inferiore del div, il primo evento trasporta nel corpo. Test con tocco a 2 dita su Chrome in osx.
johnb003

3

ecco una semplice soluzione senza jQuery che non distrugge lo scroll nativo del browser (questo è: nessuno scorrimento artificiale / brutto):

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

Demo dal vivo: http://jsfiddle.net/ibcaliax/bwmzfmq7/4/


Ho appena pubblicato una libreria NPM che implementa il codice sopra: npmjs.com/package/dontscrollthebody
Iñaki Baz Castillo

2

Ecco la mia soluzione che ho usato nelle applicazioni.

Ho disabilitato l'overflow del corpo e ho posizionato l'intero sito html all'interno dei div del contenitore. I contenitori del sito web sono in overflow e quindi l'utente può scorrere la pagina come previsto.

Ho quindi creato un div fratello (#Prevent) con uno z-index più alto che copre l'intero sito web. Poiché #Prevent ha uno z-index più alto, si sovrappone al contenitore del sito web. Quando #Prevent è visibile, il mouse non passa più con il mouse sui contenitori del sito Web, quindi lo scorrimento non è possibile.

Ovviamente puoi inserire un altro div, come il tuo modale, con uno z-index più alto di #Prevent nel markup. Ciò ti consente di creare finestre pop-up che non soffrono di problemi di scorrimento.

Questa soluzione è migliore perché non nasconde le barre di scorrimento (effetto di salto). Non richiede listener di eventi ed è facile da implementare. Funziona in tutti i browser, anche se con IE7 e 8 devi giocare (dipende dal tuo codice specifico).

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

disabilita / abilita lo scroll

PreventScroll('on'); // prevent scrolling
PreventScroll('off'); // allow scrolling

2
Considera l'idea di includere alcune informazioni sulla tua risposta, piuttosto che inserire semplicemente il codice. Cerchiamo di fornire non solo "soluzioni", ma di aiutare le persone a imparare. Dovresti spiegare cosa c'era di sbagliato nel codice originale, cosa hai fatto in modo diverso e perché le tue modifiche hanno funzionato.
Andrew Barber,

1

Avevo bisogno di aggiungere questo evento a più elementi che potrebbero avere una barra di scorrimento. Per i casi in cui non era presente alcuna barra di scorrimento, la barra di scorrimento principale non funzionava come dovrebbe. Quindi ho apportato una piccola modifica al codice @ Šime come segue:

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

Ora, solo gli elementi con una barra di scorrimento impediranno l'arresto dello scorrimento principale.


0

La versione javascript pura della risposta di Vidas, el$è il nodo DOM dell'aereo che stai scorrendo.

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

Assicurati di non attaccare nemmeno più volte! Ecco un esempio,

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

Un avvertimento , la funzione deve essere una costante, se si reinizializza la funzione ogni volta prima di collegarla, ad es. var func = function (...)il removeEventListenernon funzionerà.


0

Puoi farlo senza JavaScript. Puoi impostare lo stile su entrambi i div su position: fixede overflow-y: auto. Potrebbe essere necessario renderne uno più alto dell'altro impostando il suo z-index(se si sovrappongono).

Ecco un esempio di base su CodePen .


0

Ecco il plug-in che è utile per impedire lo scorrimento genitore durante lo scorrimento di un div specifico e ha un sacco di opzioni con cui giocare.

Controllalo qui:

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

Utilizzo

Attiva il blocco scorrimento tramite JavaScript:

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

Attiva il blocco dello scorrimento tramite markup:

    <!-- 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>

Visualizza demo


0

semplicemente offrendolo come una possibile soluzione se non pensi che l'utente avrà un'esperienza negativa sull'ovvio cambiamento. Ho semplicemente cambiato la classe di overflow del corpo in nascosto quando il mouse era sopra il div di destinazione; poi ho cambiato il div del corpo in overflow nascosto quando il mouse se ne va.

Personalmente non penso che sembri brutto, il mio codice potrebbe usare il toggle per renderlo più pulito e ci sono evidenti vantaggi nel rendere possibile questo effetto senza che l'utente se ne accorga. Quindi questa è probabilmente la risposta hacker-ultima risorsa.

//listen mouse on and mouse off for the button
pxMenu.addEventListener("mouseover", toggleA1);
pxOptContainer.addEventListener("mouseout", toggleA2);
//show / hide the pixel option menu
function toggleA1(){
  pxOptContainer.style.display = "flex";
  body.style.overflow = "hidden";
}
function toggleA2(){
  pxOptContainer.style.display = "none";
  body.style.overflow = "hidden scroll";
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.