Bagaimana cara mencegah pengguliran halaman saat menggulir elemen DIV?

94

Saya telah meninjau dan menguji berbagai fungsi untuk mencegah kemampuan tubuh untuk menggulir saat berada di dalam div dan telah menggabungkan fungsi yang seharusnya berfungsi.

$('.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;
    });
});
  • Ini menghentikan semua pengguliran di mana karena saya ingin pengguliran masih dimungkinkan di dalam wadah
  • Ini juga tidak menonaktifkan saat cuti mouse

Ada ide, atau cara yang lebih baik untuk melakukan ini?

RIK
sumber
Anda mengatur tubuh sebagai pengembalian palsu dari roda mouse, mungkin inilah masalahnya, saya kira wadah Anda ada di dalam tubuh kanan
Ricardo Binns
@ric_bfa ya tapi bagaimana memperbaikinya
RIK
alih-alih body, setel container class / id Anda
Ricardo Binns
Mungkin saya harus menjelaskan. Ketika mouse berada di dalam elemen '.scrollable' kemampuan tubuh untuk menggulir harus dinonaktifkan
RIK
4
Apakah tidak ada cara untuk melakukan ini dengan semua CSS dan tanpa JavaScript?
chharvey

Jawaban:

165

Pembaruan 2: Solusi saya didasarkan pada menonaktifkan pengguliran asli browser sama sekali (saat kursor berada di dalam DIV) dan kemudian secara manual menggulir DIV dengan JavaScript (dengan mengatur .scrollToppropertinya). Alternatif dan pendekatan IMO yang lebih baik adalah dengan hanya menonaktifkan scrolling browser secara selektif untuk mencegah scroll halaman, tetapi bukan scroll DIV. Lihat jawaban Rudie di bawah ini yang menunjukkan solusi ini.


Ini dia:

$( '.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 langsung: https://jsbin.com/howojuq/edit?js,output

Jadi Anda secara manual mengatur posisi gulir dan kemudian mencegah perilaku default (yang akan menggulir DIV atau seluruh halaman web).

Pembaruan 1: Seperti yang Chris catat dalam komentar di bawah, di versi jQuery yang lebih baru, informasi delta bersarang di dalam .originalEventobjek, yaitu jQuery tidak lagi mengeksposnya di objek Acara khusus dan kita harus mengambilnya dari objek Acara asli sebagai gantinya .

Šime Vidas
sumber
2
@RobinKnight Tidak, sepertinya tidak begitu. Berikut adalah demo yang berfungsi: jsfiddle.net/XNwbt/1 dan berikut adalah demo dengan baris-baris yang dihapus: jsfiddle.net/XNwbt/2
Šime Vidas
1
Pengguliran manual Anda mundur di Firefox 10, setidaknya di Linux dan Mac. Tampaknya berfungsi dengan benar jika Anda membuatnya -e.detail, diuji di Firefox (Mac, Linux), Safari (Mac), dan Chromium (Linux).
Anomie
1
@Anomie Itu benar. Tanda Mozilla .detailtidak sesuai dengan tanda .wheelData(yang dimiliki Chrome / IE), jadi kita harus membalikkannya secara manual. Terima kasih telah memperbaiki jawaban saya.
Šime Vidas
8
Saya menggunakan ini, terima kasih! Saya perlu menambahkan ini:if( e.originalEvent ) e = e.originalEvent;
Nikso
2
@ ŠimeVidas Saya membuat penyesuaian untuk ini setelah saya menerapkannya di perangkat lunak saya. Tidak penting tetapi mungkin menambahkan pemeriksaan kewarasan untuk properti scroll untuk mencegah terjadinya deadlock scrolling saat elemen tidak memiliki scroll. if ( $(this)[0].scrollHeight !== $(this).outerHeight() ) { //yourcode }akan dijalankan hanya jika ada scrollbar.
pengguna0000001
30

Jika Anda tidak peduli tentang kompatibilitas dengan versi IE yang lebih lama (<8), Anda dapat membuat plugin jQuery kustom dan kemudian memanggilnya pada elemen overflowing.

Solusi ini memiliki keunggulan dibandingkan yang diusulkan oleh Simime Vidas, karena tidak menimpa perilaku pengguliran - hanya memblokirnya jika diperlukan.

$.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.dll
sumber
2
Terima kasih! Saya pikir lebih tepat untuk tidak menimpa perilaku default. Untuk dukungan lintas browser dapat digunakan plugin jQuery Mouse Wheel .
Meettya
Sayangnya ini tampak bermasalah di Chrome 52 (OSX 10.10). Bekerja dengan sempurna di Safari.
sangat dingin
1
Terima kasih! Solusi lain membuat pengguliran menjadi sangat lambat dan
bermasalah
@wojcikstefan, saya mendapatkan peringatan ini di 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

Saya pikir terkadang acara mousescroll dapat dibatalkan: 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();
});

Di dalam event handler, Anda perlu mengetahui:

  • arah gulir
    d = e.originalEvent.deltaY, dir = d < 0 ? 'up' : 'down' karena angka positif berarti scrolling ke bawah
  • posisi gulir
    scrollTopuntuk atas, scrollHeight - scrollTop - offsetHeightuntuk bawah

Jika kamu

  • menggulir ke atas, dan top = 0, atau
  • menggulir ke bawah, dan bottom = 0,

batalkan acara: e.preventDefault() (dan bahkan mungkin e.stopPropagation()).

Saya pikir lebih baik untuk tidak menimpa perilaku scrolling browser. Hanya batalkan jika memungkinkan.

Mungkin tidak benar-benar xbrowser, tetapi tidak terlalu sulit. Mungkin arah gulir ganda Mac itu rumit ...

Rudie
sumber
2
bagaimana cara menerapkan fungsi yang sama untuk perangkat (tablet / ponsel) saya kira touchmove adalah peristiwa yang mengikat
ViVekH
6

Gunakan properti CSS di bawah ini overscroll-behavior: contain;untuk elemen anak

Prasoon Kumar
sumber
Semua solusi lainnya berlebihan, dibandingkan dengan aturan CSS asli ini. Selamat.
TechWisdom
3

lihat apakah ini membantu Anda:

demo: jsfiddle

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

edit:

coba ini:

    $("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
sumber
Elemen Anda terpisah, sedangkan salah satu elemen saya terkandung di elemen lainnya.
RIK
3

Solusi yang tidak terlalu hacky, menurut saya adalah mengatur overflow yang tersembunyi di tubuh saat Anda mengarahkan mouse ke div yang dapat digulir. Ini akan mencegah badan dari menggulir, tetapi efek "melompat" yang tidak diinginkan akan terjadi. Solusi berikut mengatasi hal itu:

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

Anda dapat membuat kode Anda lebih pintar jika perlu. Misalnya, Anda dapat menguji apakah bodi sudah memiliki bantalan dan jika ya, tambahkan bantalan baru ke dalamnya.

Alexandru Lighezan
sumber
3

Dalam solusi di atas ada sedikit kesalahan terkait Firefox. Dalam acara Firefox "DOMMouseScroll" tidak memiliki properti e.detail, untuk mendapatkan properti ini Anda harus menulis 'e.originalEvent.detail' berikut.

Berikut adalah solusi yang berfungsi untuk 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
sumber
Ini bekerja cukup baik kecuali saat Anda menabrak bagian atas atau bawah div yang dibawa oleh peristiwa pertama ke dalam badan. Menguji dengan sentuhan 2 jari pada chrome di osx.
johnb003
3

di sini solusi sederhana tanpa jQuery yang tidak merusak scroll asli browser (ini adalah: tidak ada scrolling buatan / jelek):

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 langsung: http://jsfiddle.net/ibcaliax/bwmzfmq7/4/

Iñaki Baz Castillo
sumber
Saya baru saja menerbitkan perpustakaan NPM yang menerapkan kode di atas: npmjs.com/package/dontscrollthebody
Iñaki Baz Castillo
2

Ini solusi yang saya gunakan dalam aplikasi.

Saya menonaktifkan body overflow dan menempatkan seluruh situs web html di dalam wadah div. Penampung situs web memiliki luapan sehingga pengguna dapat menggulir halaman seperti yang diharapkan.

Saya kemudian membuat div saudara (#Prevent) dengan indeks-z lebih tinggi yang mencakup seluruh situs web. Karena #Prevent memiliki indeks-z yang lebih tinggi, #Prevent tumpang tindih dengan penampung situs web. Saat #Prevent terlihat, mouse tidak lagi mengarahkan kursor ke penampung situs web, jadi pengguliran tidak dimungkinkan.

Anda tentu saja dapat menempatkan div lain, seperti modal Anda, dengan z-index yang lebih tinggi daripada #Prevent di markup. Ini memungkinkan Anda membuat jendela pop-up yang tidak mengalami masalah scrolling.

Solusi ini lebih baik karena tidak menyembunyikan scrollbar (efek lompatan). Itu tidak membutuhkan pendengar acara dan mudah diterapkan. Ia bekerja di semua browser, meskipun dengan IE7 & 8 Anda harus bermain-main (tergantung pada kode spesifik Anda).

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

nonaktifkan / aktifkan scroll

PreventScroll('on'); // prevent scrolling
PreventScroll('off'); // allow scrolling
David D
sumber
2
Harap pertimbangkan untuk memasukkan beberapa informasi tentang jawaban Anda, daripada hanya memposting kode. Kami mencoba memberikan tidak hanya 'perbaikan', tetapi membantu orang belajar. Anda harus menjelaskan apa yang salah dalam kode asli, apa yang Anda lakukan secara berbeda, dan mengapa perubahan Anda berhasil.
Andrew Barber
1

Saya perlu menambahkan acara ini ke beberapa elemen yang mungkin memiliki bilah gulir. Untuk kasus di mana tidak ada bilah gulir, bilah gulir utama tidak berfungsi sebagaimana mestinya. Jadi saya membuat sedikit perubahan pada kode @ Sime sebagai berikut:

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

Sekarang, hanya elemen dengan scrollbar yang akan mencegah scroll utama berhenti.

Ricky
sumber
0

Versi javascript murni dari jawaban Vidas, el$adalah simpul DOM dari bidang yang Anda gulir.

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

Pastikan Anda tidak memasang genap beberapa kali! Berikut ini contohnya,

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

Kata hati-hati , fungsi harus ada konstanta, jika Anda menginisialisasi ulang fungsi setiap kali sebelum melampirkannya, yaitu. var func = function (...)yang removeEventListenertidak akan bekerja.

srcspider
sumber
0

Anda dapat melakukan ini tanpa JavaScript. Anda dapat menyetel gaya pada kedua div menjadi position: fixeddan overflow-y: auto. Anda mungkin perlu membuat salah satunya lebih tinggi dari yang lain dengan menyetelnyaz-index (jika tumpang tindih).

Berikut adalah contoh dasar di CodePen .

Carl Smith
sumber
0

Berikut adalah plugin yang berguna untuk mencegah pengguliran induk saat menggulir div tertentu dan memiliki banyak opsi untuk dimainkan.

Lihat disini:

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

Pemakaian

Memicu Scroll Lock melalui JavaScript:

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

Memicu Scroll Lock melalui 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>

Lihat Demo

MR_AMDEV
sumber
0

hanya menawarkan ini sebagai solusi yang mungkin jika menurut Anda pengguna tidak akan memiliki pengalaman negatif pada perubahan yang jelas. Saya hanya mengubah kelas overflow tubuh menjadi tersembunyi ketika mouse berada di atas div target; lalu saya mengubah div tubuh menjadi luapan tersembunyi saat mouse pergi.

Secara pribadi saya tidak berpikir itu terlihat buruk, kode saya dapat menggunakan toggle untuk membuatnya lebih bersih, dan ada manfaat yang jelas untuk membuat efek ini mungkin terjadi tanpa pengguna menyadarinya. Jadi ini mungkin jawaban terakhir yang hackish-last-resort.

//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
sumber