Bir DIV öğesini kaydırırken sayfanın kaymasını nasıl önleyebilirim?


94

Bir div içindeyken vücudun kaydırma yeteneğini önlemek için çeşitli işlevleri gözden geçirdim ve test ettim ve çalışması gereken bir işlevi birleştirdim.

$('.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;
    });
});
  • Bu, kapsayıcı içinde kaydırmanın hala mümkün olmasını istediğim yerde tüm kaydırmayı durduruyor
  • Bu, fare bırakıldığında da devre dışı bırakılmaz

Herhangi bir fikir veya bunu yapmanın daha iyi yolları?


gövdeyi fare tekerleğinden yanlış dönüş olarak ayarladınız, belki sorun budur, sanırım
kabınız

@ric_bfa evet ama nasıl düzeltilir
RIK

bunun yerine gövde, sınıf / kimlik kapsayıcınızı ayarlayın
Ricardo Binns

Belki de açıklığa kavuşturmalıyım. Fare ". Kaydırılabilir" öğesinin içindeyken, vücudun kaydırma yeteneği devre dışı bırakılmalıdır
RIK

4
Bunu tüm CSS ile ve JavaScript olmadan yapmanın bir yolu yok mu?
chharvey

Yanıtlar:


167

Güncelleme 2: Çözümüm, tarayıcının yerel kaydırmasını tamamen devre dışı bırakmaya (imleç DIV'nin içindeyken) ve ardından DIV'yi JavaScript ile manuel olarak kaydırmaya ( .scrollTopözelliğini ayarlayarak ) dayanıyor . Alternatif ve daha iyi bir IMO yaklaşımı, DIV kaydırmasını değil, sayfa kaydırmasını önlemek için tarayıcının kaydırmasını yalnızca seçici olarak devre dışı bırakmak olabilir. Bu çözümü gösteren aşağıdaki Rudie'nin cevabına göz atın.


Hadi bakalım:

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

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

Canlı demo: https://jsbin.com/howojuq/edit?js,output

Böylece, kaydırma konumunu manuel olarak ayarlarsınız ve ardından varsayılan davranışı engellersiniz (bu, DIV'yi veya tüm web sayfasını kaydırmak olacaktır).

Güncelleme 1: Chris'in aşağıdaki yorumlarda belirttiği gibi, jQuery'nin daha yeni sürümlerinde delta bilgileri .originalEventnesnenin içine yerleştirilmiştir , yani jQuery artık onu özel Event nesnesinde göstermez ve bunun yerine yerel Event nesnesinden almamız gerekir. .


2
@RobinKnight Hayır, öyle görünmüyor. İşte çalışan demo: jsfiddle.net/XNwbt/1 ve işte bu satırların kaldırıldığı demo: jsfiddle.net/XNwbt/2
Šime Vidas

1
Manuel kaydırmanız Firefox 10'da, en azından Linux ve Mac'te geriye doğru. Bunu yaparsanız -e.detail, Firefox (Mac, Linux), Safari (Mac) ve Chromium (Linux) ile test edildiğinde düzgün çalışıyor gibi görünüyor .
Anomie

1
@Anomie Bu doğru. Mozilla'nın .detailişareti .wheelData(Chrome / IE'nin sahip olduğu) işaretiyle uyuşmuyor , bu yüzden onu manuel olarak tersine çevirmeliyiz. Cevabımı düzelttiğiniz için teşekkürler.
Šime Vidas

8
Bunu kullandım, teşekkürler! Bu tho eklemek için gerekli:if( e.originalEvent ) e = e.originalEvent;
Nikso

2
@ ŠimeVidas Yazılımıma uyguladıktan sonra buna bir ayarlama yaptım. Kritik değildir, ancak öğenin kaydırması olmadığında kilitlenmenin kaydırılmasını önlemek için kaydırma özelliği için muhtemelen bir mantık denetimi ekleyin. if ( $(this)[0].scrollHeight !== $(this).outerHeight() ) { //yourcode }yalnızca bir kaydırma çubuğu olduğunda çalıştırılır.
user0000001

30

Eski IE sürümleriyle (<8) uyumluluk umurunuzda değilse, özel bir jQuery eklentisi oluşturabilir ve ardından onu taşan öğede çağırabilirsiniz.

Bu çözüm, kaydırma davranışının üzerine yazmadığı için Šime Vidas'ın önerisine göre bir avantaja sahip - sadece uygun olduğunda onu engelliyor.

$.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
Teşekkürler! Varsayılan davranışın üzerine yazmamanın daha uygun olduğunu düşünüyorum. Tarayıcılar arası destek için jQuery Mouse Wheel eklentisi kullanılabilir.
Meettya

Ne yazık ki bu, Chrome 52'de (OSX 10.10) kusurlu görünüyor. Yine de Safari'de mükemmel çalışıyor.
soğuk

1
Teşekkür ederim! Diğer çözümler kaydırmayı çok yavaş ve hatalı yaptı
agrublev

@wojcikstefan, bu uyarıyı kromdan alıyorum:[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

Bazen mousescroll olayını iptal etmenin mümkün olduğunu düşünüyorum: 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();
});

Olay işleyicisinin içinde bilmeniz gerekenler:

  • kaydırma yönü,
    d = e.originalEvent.deltaY, dir = d < 0 ? 'up' : 'down' çünkü pozitif bir sayı aşağı kaydırma anlamına gelir

  • scrollTopüst için kaydırma konumu , scrollHeight - scrollTop - offsetHeightalt için

Eğer öyleysen

  • yukarı kaydırma ve top = 0, veya
  • aşağı kaydırma ve bottom = 0,

etkinliği iptal edin: e.preventDefault()(ve hatta belki e.stopPropagation()).

Tarayıcının kaydırma davranışını geçersiz kılmamanın daha iyi olacağını düşünüyorum. Yalnızca uygun olduğunda iptal edin.

Muhtemelen mükemmel bir xbrowser değil, ama çok da zor olamaz. Belki de Mac'in ikili kaydırma yönü zor olabilir ...


2
cihazlar (tabletler / telefonlar) için aynı işlevi nasıl uygulayabilirim sanırım
touchmove

13

Alt overscroll-behavior: contain;öğeye CSS özelliğini kullanın


Diğer tüm çözümler, bu yerel CSS kuralına kıyasla aşırıdır. Güzel yapılmış.
TechWisdom

3

Bunun size yardımcı olup olmadığına bakın:

demo: jsfiddle

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

Düzenle:

bunu dene:

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

Öğeleriniz ayrı, benimkilerden biri diğerinde.
RIK

3

Bence, kaydırılabilir div'in üzerine fareyi getirdiğinizde taşmayı gövdede gizli tutmaktır. Bu, gövdenin kaymasını önleyecek, ancak istenmeyen bir "atlama" etkisi oluşacaktır. Aşağıdaki çözüm bunun etrafında çalışır:

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

Gerekirse kodunuzu daha akıllı hale getirebilirsiniz. Örneğin, gövdenin halihazırda bir dolgusu olup olmadığını test edebilir ve varsa, yeni dolguyu buna ekleyebilirsiniz.


3

Yukarıdaki çözümde Firefox ile ilgili küçük bir hata var. Firefox'ta "DOMMouseScroll" olayının e.detail özelliği yoktur, bu özelliği almak için aşağıdaki 'e.originalEvent.detail' yazmalısınız.

İşte Firefox için çalışan bir çözüm:

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

Bu, div'in üstüne veya altına çarptığınızda ilk olayın vücuda taşınması dışında oldukça iyi çalışır. Osx'te krom üzerinde 2 parmakla dokunarak test etme.
johnb003

3

burada jQuery içermeyen ve tarayıcının yerel kaydırmasını yok etmeyen basit bir çözüm (bu: yapay / çirkin kaydırma yok):

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

Canlı demo: http://jsfiddle.net/ibcaliax/bwmzfmq7/4/


Yukarıdaki kodu uygulayan bir NPM kitaplığı yayınladım: npmjs.com/package/dontscrollthebody
Iñaki Baz Castillo

2

İşte uygulamalarda kullandığım çözümüm.

Gövde taşmasını devre dışı bıraktım ve tüm web sitesi html'sini kapsayıcı div'lerin içine yerleştirdim. Web sitesi kapsayıcılarında taşma vardır ve bu nedenle kullanıcı, sayfayı beklendiği gibi kaydırabilir.

Daha sonra tüm web sitesini kapsayan daha yüksek bir z-endeksine sahip bir kardeş div (#Prevent) oluşturdum. #Prevent daha yüksek bir z-endeksine sahip olduğundan, web sitesi kapsayıcısıyla çakışır. #Prevent göründüğünde, fare artık web sitesi kapsayıcılarının üzerine gelmez, bu nedenle kaydırma yapmak mümkün değildir.

Elbette, modalınız gibi, işaretlemede #Prevent'den daha yüksek bir z-endeksine sahip başka bir div yerleştirebilirsiniz. Bu, kaydırma sorunları yaşamayan açılır pencereler oluşturmanıza olanak tanır.

Bu çözüm daha iyidir çünkü kaydırma çubuklarını gizlemez (atlama etkisi). Olay dinleyicileri gerektirmez ve uygulaması kolaydır. Tüm tarayıcılarda çalışır, ancak IE7 & 8 ile oynamanız gerekir (özel kodunuza bağlıdır).

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

kaydırmayı devre dışı bırak / etkinleştir

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

2
Lütfen kodu göndermek yerine cevabınızla ilgili bazı bilgiler eklemeyi düşünün. Sadece 'düzeltmeler' sağlamaya değil, aynı zamanda insanların öğrenmesine yardımcı olmaya çalışıyoruz. Orijinal kodda neyin yanlış olduğunu, neyi farklı yaptığınızı ve değişikliklerinizin neden işe yaradığını açıklamalısınız.
Andrew Barber

1

Bu olayı bir kaydırma çubuğuna sahip olabilecek birden çok öğeye eklemem gerekiyordu. Kaydırma çubuğunun olmadığı durumlarda, ana kaydırma çubuğu olması gerektiği gibi çalışmadı. Bu yüzden @ Šime kodunda aşağıdaki gibi küçük bir değişiklik yaptım:

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

Artık, yalnızca kaydırma çubuğuna sahip öğeler ana kaydırmanın durmaya başlamasını engelleyecektir.


0

Vidas'ın cevabının saf javascript versiyonu, kaydırdığınız el$uçağın DOM düğümüdür.

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

Birden çok kez eklemediğinizden emin olun! İşte bir örnek,

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

Dikkat ederseniz, fonksiyonu her eklemeden önce yeniden başlatırsanız, fonksiyonun bir sabit olması gerekir, yani. çalışmayacaktır.var func = function (...)removeEventListener


0

Bunu JavaScript olmadan yapabilirsiniz. Sen hiç divs hem stilini ayarlayabilirsiniz position: fixedve overflow-y: auto. Birini diğerinden daha yükseğe ayarlayarak z-index(çakışıyorlarsa) yapmanız gerekebilir .

İşte CodePen ile ilgili temel bir örnek .


0

İşte belirli bir div'i kaydırırken ebeveyn kaydırmasını önlemek için yararlı olan ve oynamak için bir sürü seçeneğe sahip olan eklenti.

Buradan kontrol edin:

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

Kullanım

JavaScript aracılığıyla Kaydırma Kilidini Tetikleyin:

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

İşaretleme ile Kaydırma Kilidini Tetikle:

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

Demoyu Görüntüle


0

Kullanıcının bariz değişiklik konusunda olumsuz bir deneyim yaşayacağını düşünmüyorsanız, bunu olası bir çözüm olarak sunmanız yeterli. Basitçe, fare hedef div üzerindeyken vücudun taşma sınıfını gizli olarak değiştirdim; sonra fare ayrıldığında vücudun bölmesini gizli taşma olarak değiştirdim.

Şahsen bunun kötü göründüğünü düşünmüyorum, kodum onu ​​daha temiz hale getirmek için geçiş kullanabilir ve bu etkiyi kullanıcının farkında olmadan mümkün kılmanın bariz faydaları vardır. Yani bu muhtemelen son çare cevabıdır.

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.