Wie kann das Scrollen von Seiten beim Scrollen eines DIV-Elements verhindert werden?

94

Ich habe die verschiedenen Funktionen überprüft und getestet, um zu verhindern, dass der Körper in einem Div scrollen kann, und eine Funktion kombiniert, die funktionieren sollte.

$('.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;
    });
});
  • Dadurch wird das Scrollen gestoppt, da das Scrollen im Container weiterhin möglich sein soll
  • Dies wird auch beim Verlassen der Maus nicht deaktiviert

Irgendwelche Ideen oder bessere Möglichkeiten, dies zu tun?

RIK
quelle
Sie setzen den Körper als falsch vom Mausrad zurück, vielleicht ist dies das Problem, ich nehme an, Ihr Behälter befindet sich im Körper richtig
Ricardo Binns
@ric_bfa ja, aber wie man es behebt
RIK
Stellen Sie stattdessen Ihren Klassen- / ID-Container ein
Ricardo Binns
Vielleicht sollte ich das klarstellen. Wenn sich die Maus innerhalb des Elements '.scrollable' befindet, sollte die Fähigkeit des Körpers zum Scrollen deaktiviert sein
RIK
4
Gibt es keine Möglichkeit, dies mit allen CSS und ohne JavaScript zu tun?
Chharvey

Antworten:

165

Update 2: Meine Lösung basiert darauf, das native Scrollen des Browsers vollständig zu deaktivieren (wenn sich der Cursor innerhalb des DIV befindet) und das DIV dann manuell mit JavaScript zu scrollen (indem seine .scrollTopEigenschaft festgelegt wird). Ein alternativer und IMO-besserer Ansatz wäre, das Scrollen des Browsers nur selektiv zu deaktivieren, um das Scrollen der Seite zu verhindern, nicht jedoch das Scrollen des DIV. Schauen Sie sich Rudies Antwort unten an, die diese Lösung demonstriert.


Bitte schön:

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

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

Live-Demo: https://jsbin.com/howojuq/edit?js,output

Sie stellen also die Bildlaufposition manuell ein und verhindern dann nur das Standardverhalten (das Scrollen des DIV oder der gesamten Webseite).

Update 1: Wie Chris in den Kommentaren unten bemerkt hat, sind in neueren Versionen von jQuery die Delta-Informationen im .originalEventObjekt verschachtelt , dh jQuery macht sie nicht mehr in seinem benutzerdefinierten Ereignisobjekt verfügbar und wir müssen sie stattdessen aus dem nativen Ereignisobjekt abrufen .

Šime Vidas
quelle
2
@RobinKnight Nein, scheint nicht so. Hier ist die funktionierende Demo: jsfiddle.net/XNwbt/1 und hier ist die Demo mit diesen entfernten Zeilen: jsfiddle.net/XNwbt/2
Šime Vidas
1
Ihr manuelles Scrollen erfolgt unter Firefox 10 rückwärts, zumindest unter Linux und Mac. Scheint richtig zu funktionieren, wenn Sie das machen -e.detail, getestet in Firefox (Mac, Linux), Safari (Mac) und Chromium (Linux).
Anomie
1
@ Anomie Das ist richtig. Das Vorzeichen von Mozilla .detailentspricht nicht dem Vorzeichen von .wheelData(was Chrome / IE hat), daher müssen wir es manuell invertieren. Danke, dass du meine Antwort korrigiert hast.
Šime Vidas
8
Ich habe das benutzt, danke! Ich musste folgendes hinzufügen:if( e.originalEvent ) e = e.originalEvent;
Nikso
2
@ ŠimeVidas Ich habe dies angepasst, nachdem ich es in meine Software implementiert habe. Nicht kritisch, aber möglicherweise eine Überprüfung der Integrität der scroll-Eigenschaft hinzufügen, um das Scrollen durch Deadlocks zu verhindern, wenn das Element keinen Scroll hat. if ( $(this)[0].scrollHeight !== $(this).outerHeight() ) { //yourcode }wird nur ausgeführt, wenn eine Bildlaufleiste vorhanden ist.
user0000001
30

Wenn Sie sich nicht für die Kompatibilität mit älteren IE-Versionen (<8) interessieren, können Sie ein benutzerdefiniertes jQuery-Plugin erstellen und es dann für das überlaufende Element aufrufen.

Diese Lösung hat einen Vorteil gegenüber der von Šime Vidas vorgeschlagenen, da sie das Bildlaufverhalten nicht überschreibt, sondern nur bei Bedarf blockiert.

$.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();
wojcikstefan
quelle
2
Vielen Dank! Ich denke, es ist angemessener, das Standardverhalten nicht zu überschreiben. Zur browserübergreifenden Unterstützung kann das jQuery Mouse Wheel- Plugin verwendet werden.
Meettya
Leider scheint dies in Chrome 52 (OSX 10.10) ein Problem zu sein. Funktioniert jedoch perfekt in Safari.
Frosty
1
Danke dir! Die anderen Lösungen machten das Scrollen super langsam und
fehlerhaft
@wojcikstefan, ich bekomme diese Warnung in Chrom:[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

Ich denke, es ist manchmal möglich, das Mousescroll-Event abzusagen: 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();
});

Im Event-Handler müssen Sie Folgendes wissen:

  • Bildlaufrichtung,
    d = e.originalEvent.deltaY, dir = d < 0 ? 'up' : 'down' da eine positive Zahl einen Bildlauf nach unten bedeutet
  • Bildlaufposition
    scrollTopfür oben, scrollHeight - scrollTop - offsetHeightfür unten

Wenn du bist

  • nach oben scrollen und top = 0, oder
  • nach unten scrollen und bottom = 0,

Veranstaltung abbrechen: e.preventDefault()(und vielleicht sogar e.stopPropagation()).

Ich denke, es ist besser, das Bildlaufverhalten des Browsers nicht zu überschreiben. Kündigen Sie es nur, wenn zutreffend.

Es ist wahrscheinlich nicht perfekt xbrowser, aber es kann nicht sehr schwer sein. Vielleicht ist Macs doppelte Bildlaufrichtung jedoch schwierig ...

Rudie
quelle
2
Wie implementiere ich die gleiche Funktionalität für Geräte (Tablets / Telefone)? Ich denke, Touchmove ist das Bindungsereignis
ViVekH
6

Verwenden Sie die folgende CSS-Eigenschaft overscroll-behavior: contain;für das untergeordnete Element

Prasoon Kumar
quelle
Alle anderen Lösungen sind im Vergleich zu dieser nativen CSS-Regel übertrieben. Schön gemacht.
TechWisdom
3

Sehen Sie, ob dies Ihnen hilft:

Demo: jsfiddle

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

bearbeiten:

Versuche dies:

    $("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;
           });
       }
    });
Ricardo Binns
quelle
Deine Elemente sind getrennt, während eines von meinen im anderen enthalten ist.
RIK
3

Eine weniger hackige Lösung ist meiner Meinung nach, den Überlauf auf dem Körper zu verbergen, wenn Sie mit der Maus über das scrollbare Div fahren. Dies verhindert, dass der Körper scrollt, aber es tritt ein unerwünschter "Springen" -Effekt auf. Die folgende Lösung umgeht das:

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

Sie können Ihren Code bei Bedarf intelligenter gestalten. Sie können beispielsweise testen, ob der Körper bereits eine Polsterung hat, und wenn ja, die neue Polsterung hinzufügen.

Alexandru Lighezan
quelle
3

In der obigen Lösung gibt es einen kleinen Fehler in Bezug auf Firefox. In Firefox hat das Ereignis "DOMMouseScroll" keine e.detail-Eigenschaft. Um diese Eigenschaft zu erhalten, sollten Sie das folgende 'e.originalEvent.detail' schreiben.

Hier ist eine funktionierende Lösung für 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;
};
Vladimir Kuzmin
quelle
Dies funktioniert ziemlich gut, außer wenn Sie gegen die Ober- oder Unterseite des Div stoßen, trägt das erste Ereignis in den Körper. Testen mit 2-Finger-Touch auf Chrom in OSX.
Johnb003
3

Hier eine einfache Lösung ohne jQuery, die den nativen Bildlauf des Browsers nicht zerstört (dies ist: kein künstliches / hässliches Scrollen):

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

Live-Demo: http://jsfiddle.net/ibcaliax/bwmzfmq7/4/

Iñaki Baz Castillo
quelle
Ich habe gerade eine NPM-Bibliothek veröffentlicht, die den obigen Code implementiert: npmjs.com/package/dontscrollthebody
Iñaki Baz Castillo
2

Hier ist meine Lösung, die ich in Anwendungen verwendet habe.

Ich habe den Body-Überlauf deaktiviert und die gesamte HTML-Website in Container-Divs platziert. Die Website-Container haben einen Überlauf und daher kann der Benutzer die Seite wie erwartet scrollen.

Ich habe dann ein Geschwister-Div (#Prevent) mit einem höheren Z-Index erstellt, der die gesamte Website abdeckt. Da #Prevent einen höheren Z-Index hat, überlappt es den Website-Container. Wenn #Prevent sichtbar ist, schwebt die Maus nicht mehr über den Website-Containern, sodass kein Bildlauf möglich ist.

Sie können natürlich ein anderes Div, wie z. B. Ihr Modal, mit einem höheren Z-Index als #Prevent in das Markup einfügen. Auf diese Weise können Sie Popup-Fenster erstellen, in denen keine Bildlaufprobleme auftreten.

Diese Lösung ist besser, da die Bildlaufleisten nicht ausgeblendet werden (Sprungeffekt). Es erfordert keine Ereignis-Listener und ist einfach zu implementieren. Es funktioniert in allen Browsern, obwohl Sie mit IE7 & 8 herumspielen müssen (abhängig von Ihrem spezifischen Code).

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

Deaktivieren / Aktivieren Sie den Bildlauf

PreventScroll('on'); // prevent scrolling
PreventScroll('off'); // allow scrolling
David D.
quelle
2
Bitte geben Sie einige Informationen zu Ihrer Antwort an, anstatt nur den Code zu veröffentlichen. Wir versuchen, nicht nur "Korrekturen" bereitzustellen, sondern den Menschen beim Lernen zu helfen. Sie sollten erklären, was im ursprünglichen Code falsch war, was Sie anders gemacht haben und warum Ihre Änderung (en) funktioniert haben.
Andrew Barber
1

Ich musste dieses Ereignis mehreren Elementen hinzufügen, die möglicherweise eine Bildlaufleiste haben. In den Fällen, in denen keine Bildlaufleiste vorhanden war, funktionierte die Hauptbildlaufleiste nicht ordnungsgemäß. Also habe ich eine kleine Änderung am @ Šime-Code vorgenommen:

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

Jetzt verhindern nur Elemente mit einer Bildlaufleiste, dass der Hauptscroll gestoppt wird.

Ricky
quelle
0

Die reine Javascript-Version von Vidas 'Antwort el$ist der DOM-Knoten der Ebene, durch die Sie scrollen.

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

Stellen Sie sicher, dass Sie die gerade nicht mehrmals anbringen! Hier ist ein Beispiel,

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

Achtung , die Funktion dort muss eine Konstante sein, wenn Sie die Funktion jedes Mal neu initialisieren, bevor Sie sie anhängen, d. H. var func = function (...)das removeEventListenerwird nicht funktionieren.

srcspider
quelle
0

Sie können dies ohne JavaScript tun. Sie können den Stil für beide Divs auf position: fixedund einstellen overflow-y: auto. Möglicherweise müssen Sie einen von ihnen höher als den anderen machen, indem Sie ihn einstellen z-index(wenn sie sich überlappen).

Hier ist ein grundlegendes Beispiel für CodePen .

Carl Smith
quelle
0

Hier ist das Plugin, das nützlich ist, um das Scrollen der Eltern beim Scrollen eines bestimmten Divs zu verhindern, und das eine Reihe von Optionen zum Spielen bietet.

Schau es dir hier an:

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

Verwendung

Scroll Lock über JavaScript auslösen:

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

Scroll Lock über Markup auslösen:

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

Ansicht Demo

MR_AMDEV
quelle
0

Bieten Sie dies nur als mögliche Lösung an, wenn Sie nicht glauben, dass der Benutzer die offensichtliche Änderung negativ beurteilen wird. Ich habe einfach die Überlaufklasse des Körpers in versteckt geändert, als sich die Maus über dem Ziel-Div befand. dann habe ich das div des Körpers in versteckten Überlauf geändert, wenn die Maus geht.

Persönlich denke ich nicht, dass es schlecht aussieht, mein Code könnte umgeschaltet werden, um ihn sauberer zu machen, und es gibt offensichtliche Vorteile, diesen Effekt zu ermöglichen, ohne dass der Benutzer es merkt. Das ist also wahrscheinlich die letzte Antwort.

//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";
}
Thuperman
quelle