Làm cách nào để ngăn cuộn trang khi cuộn phần tử DIV?


94

Tôi đã xem xét và thử nghiệm các chức năng khác nhau để ngăn khả năng cơ thể cuộn trong khi bên trong một div và đã kết hợp một chức năng sẽ hoạt động.

$('.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;
    });
});
  • Thao tác này sẽ dừng tất cả việc cuộn ở nơi mà tôi muốn vẫn có thể cuộn bên trong vùng chứa
  • Điều này cũng không tắt khi di chuột

Bất kỳ ý tưởng, hoặc cách tốt hơn để làm điều này?


u thiết lập các cơ thể như trở lại sai từ xoay chuột, có lẽ đây là vấn đề, tôi giả sử container của bạn là bên trong cơ thể đúng
Ricardo Binns

@ric_bfa vâng nhưng làm thế nào để khắc phục nó
RIK

thay vì cơ thể, thiết lập chứa lớp / id của bạn
Ricardo Binns

Có lẽ tôi nên làm rõ. Khi con chuột ở bên trong phần tử '.scrollable', khả năng cuộn của cơ thể sẽ bị vô hiệu hóa
RIK 29/09

4
Không có cách nào để làm điều này với tất cả CSS và không có JavaScript?
chharvey

Câu trả lời:


165

Cập nhật 2: Giải pháp của tôi dựa trên việc vô hiệu hóa hoàn toàn tính năng cuộn gốc của trình duyệt (khi con trỏ ở bên trong DIV) và sau đó cuộn DIV bằng JavaScript theo cách thủ công (bằng cách đặt thuộc tính của nó .scrollTop). Một phương pháp thay thế và IMO tốt hơn sẽ là chỉ vô hiệu hóa có chọn lọc tính năng cuộn của trình duyệt để ngăn việc cuộn trang chứ không phải cuộn DIV. Hãy xem câu trả lời của Rudie dưới đây minh chứng cho giải pháp này.


Của bạn đây:

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

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

Bản demo trực tiếp: https://jsbin.com/howojuq/edit?js,output

Vì vậy, bạn đặt vị trí cuộn theo cách thủ công và sau đó chỉ ngăn hành vi mặc định (sẽ là cuộn DIV hoặc toàn bộ trang web).

Cập nhật 1: Như Chris đã lưu ý trong các nhận xét bên dưới, trong các phiên bản mới hơn của jQuery, thông tin delta được lồng trong .originalEventđối tượng, tức là jQuery không hiển thị nó trong đối tượng Sự kiện tùy chỉnh của nó nữa và chúng tôi phải truy xuất nó từ đối tượng Sự kiện gốc thay thế .


2
@RobinKnight Không, có vẻ không phải vậy. Dưới đây là bản demo làm việc: jsfiddle.net/XNwbt/1 và đây là bản demo với những dòng loại bỏ: jsfiddle.net/XNwbt/2
Šime Vidas

1
Thao tác cuộn thủ công của bạn đã trở lại trên Firefox 10, ít nhất là trên Linux và Mac. Có vẻ như hoạt động chính xác nếu bạn thực hiện điều đó -e.detail, được thử nghiệm trong Firefox (Mac, Linux), Safari (Mac) và Chromium (Linux).
Anomie

1
@Anomie Điều đó chính xác. Dấu hiệu của Mozilla .detailkhông tương ứng với dấu của .wheelData(đó là những gì Chrome / IE có), vì vậy chúng tôi phải đảo ngược nó theo cách thủ công. Cảm ơn vì đã sửa câu trả lời của tôi.
Šime Vidas

8
Tôi đã sử dụng cái này, cảm ơn! Tôi cần thêm tho này:if( e.originalEvent ) e = e.originalEvent;
Nikso

2
@ ŠimeVidas Tôi đã điều chỉnh điều này sau khi triển khai nó trong phần mềm của mình. Không quan trọng nhưng có thể thêm một kiểm tra tỉnh táo cho thuộc tính cuộn để ngăn chặn việc cuộn bế tắc khi phần tử không có cuộn. if ( $(this)[0].scrollHeight !== $(this).outerHeight() ) { //yourcode }sẽ chỉ thực thi khi có thanh cuộn.
user0000001 12/1213

30

Nếu bạn không quan tâm đến khả năng tương thích với các phiên bản IE cũ hơn (<8), bạn có thể tạo một plugin jQuery tùy chỉnh và sau đó gọi nó trên phần tử tràn.

Giải pháp này có lợi thế hơn so với giải pháp Vime Vidas được đề xuất, vì nó không ghi đè hành vi cuộn - nó chỉ chặn nó khi thích hợp.

$.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
Cảm ơn! Tôi nghĩ rằng nó thích hợp hơn để không ghi đè hành vi mặc định. Để hỗ trợ nhiều trình duyệt, bạn có thể sử dụng plugin jQuery Mouse Wheel .
Meettya

Thật không may, điều này có vẻ trục trặc trong Chrome 52 (OSX 10.10). Tuy nhiên, hoạt động hoàn hảo trong Safari.
băng giá

1
Cảm ơn bạn! Các giải pháp khác làm cho cuộn siêu chậm và lỗi
agrublev

@wojcikstefan, tôi nhận được cảnh báo này trong 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

Tôi nghĩ đôi khi có thể hủy sự kiện 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();
});

Bên trong trình xử lý sự kiện, bạn sẽ cần biết:

  • hướng cuộn
    d = e.originalEvent.deltaY, dir = d < 0 ? 'up' : 'down' vì một số dương có nghĩa là cuộn xuống
  • vị trí cuộn
    scrollTopcho trên cùng, scrollHeight - scrollTop - offsetHeightcho dưới cùng

Nếu bạn

  • cuộn lên, và top = 0hoặc
  • cuộn xuống và bottom = 0,

hủy sự kiện: e.preventDefault()(và thậm chí có thể e.stopPropagation()).

Tôi nghĩ tốt hơn là không ghi đè hành vi cuộn của trình duyệt. Chỉ hủy bỏ khi có thể.

Nó không phải là xbrowser hoàn hảo, nhưng nó không khó lắm. Có lẽ hướng cuộn kép của Mac hơi phức tạp ...


2
làm thế nào để thực hiện các chức năng tương tự cho các thiết bị (tablet / điện thoại) tôi đoán touchmove là sự kiện ràng buộc
ViVekH

6

Sử dụng thuộc tính CSS bên dưới overscroll-behavior: contain;cho phần tử con


Tất cả các giải pháp khác là quá mức cần thiết, so với quy tắc CSS gốc này. Tốt lắm.
TechWisdom

3

xem điều này có giúp bạn không:

demo: jsfiddle

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

biên tập:

thử cái này:

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

Các phần tử của bạn là riêng biệt, trong khi một phần tử của tôi được chứa trong phần tử kia.
RIK

3

Một giải pháp ít hack hơn, theo ý kiến ​​của tôi là đặt phần tràn ẩn trên phần thân khi bạn di chuột qua div có thể cuộn. Điều này sẽ giúp cơ thể không cuộn được mà xảy ra hiệu ứng “nhảy” không mong muốn. Giải pháp sau đây hoạt động xung quanh điều đó:

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

Bạn có thể làm cho mã của mình thông minh hơn nếu cần. Ví dụ: bạn có thể kiểm tra xem cơ thể đã có lớp đệm chưa và nếu có, hãy thêm lớp đệm mới vào đó.


3

Trong giải pháp trên, có một lỗi nhỏ liên quan đến Firefox. Trong sự kiện "DOMMouseScroll" của Firefox không có thuộc tính e.detail, để có được thuộc tính này, bạn nên viết 'e.originalEvent.detail' sau.

Đây là một giải pháp hoạt động cho 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;
};

Điều này hoạt động khá tốt ngoại trừ khi bạn đâm vào đầu hoặc cuối của div mà sự kiện đầu tiên mang vào phần thân. Thử nghiệm bằng cách chạm 2 ngón tay trên chrome trong osx.
johnb003

3

đây là một giải pháp đơn giản mà không có jQuery không phá hủy cuộn gốc của trình duyệt (đây là: không có cuộn giả tạo / xấu xí):

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

Bản demo trực tiếp: http://jsfiddle.net/ibcaliax/bwmzfmq7/4/


Tôi vừa mới công bố một thư viện NPM thực hiện đoạn code trên: npmjs.com/package/dontscrollthebody
Iñaki Baz Castillo

2

Đây là giải pháp của tôi mà tôi đã sử dụng trong các ứng dụng.

Tôi đã vô hiệu hóa phần tràn nội dung và đặt toàn bộ trang web html bên trong container div. Vùng chứa trang web bị tràn và do đó người dùng có thể cuộn trang như mong đợi.

Sau đó, tôi đã tạo một div anh chị em (#Prevent) với chỉ số z cao hơn bao phủ toàn bộ trang web. Vì #Prevent có chỉ số z cao hơn nên nó chồng lên vùng chứa trang web. Khi #Prevent hiển thị, chuột không còn di chuột qua các vùng chứa trang web, do đó, không thể cuộn.

Tất nhiên, bạn có thể đặt một div khác, chẳng hạn như phương thức của bạn, với chỉ số z cao hơn #Prevent trong đánh dấu. Điều này cho phép bạn tạo các cửa sổ bật lên mà không bị các vấn đề về cuộn.

Giải pháp này tốt hơn vì nó không ẩn các thanh cuộn (ảnh hưởng đến việc nhảy). Nó không yêu cầu người nghe sự kiện và rất dễ thực hiện. Nó hoạt động trên tất cả các trình duyệt, mặc dù với IE7 & 8, bạn phải chơi xung quanh (tùy thuộc vào mã cụ thể của bạn).

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

tắt / bật cuộn

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

2
Vui lòng xem xét bao gồm một số thông tin về câu trả lời của bạn, thay vì chỉ đăng mã. Chúng tôi cố gắng không chỉ cung cấp 'bản sửa lỗi' mà còn giúp mọi người học hỏi. Bạn nên giải thích điều gì đã sai trong mã gốc, những gì bạn đã làm khác đi và tại sao (các) thay đổi của bạn hoạt động.
Andrew Barber

1

Tôi cần thêm sự kiện này vào nhiều phần tử có thể có thanh cuộn. Đối với những trường hợp không có thanh cuộn nào, thanh cuộn chính không hoạt động như bình thường. Vì vậy, tôi đã thực hiện một thay đổi nhỏ đối với mã @ Šime như sau:

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

Bây giờ, chỉ các phần tử có thanh cuộn mới ngăn cuộn chính bắt đầu dừng lại.


0

Phiên bản javascript thuần túy của câu trả lời của Vidas, el$là nút DOM của mặt phẳng bạn đang cuộn.

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

Hãy chắc chắn rằng bạn không đính kèm thậm chí nhiều lần! Đây là một ví dụ,

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

Lưu ý , hàm ở đó cần phải là một hằng số, nếu bạn khởi động lại hàm mỗi lần trước khi đính kèm, tức là. var func = function (...)ý removeEventListenerchí không hoạt động.


0

Bạn có thể làm điều này mà không cần JavaScript. Bạn có thể đặt kiểu trên cả div sang position: fixedoverflow-y: auto. Bạn có thể cần làm cho một trong số chúng cao hơn cái kia bằng cách đặtz-index (nếu chúng chồng lên nhau).

Đây là một ví dụ cơ bản về CodePen .


0

Đây là plugin hữu ích để ngăn chặn cuộn cha khi cuộn một div cụ thể và có một loạt các tùy chọn để chơi cùng.

Kiểm tra nó tại đây:

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

Sử dụng

Kích hoạt Khóa cuộn qua JavaScript:

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

Kích hoạt Khóa cuộn qua Đánh dấu:

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

Xem Demo


0

chỉ đưa ra giải pháp này như một giải pháp khả thi nếu bạn không nghĩ rằng người dùng sẽ có trải nghiệm tiêu cực về sự thay đổi rõ ràng. Tôi chỉ đơn giản là thay đổi lớp tràn của cơ thể thành ẩn khi chuột ở trên div đích; sau đó tôi đã thay đổi div của cơ thể thành phần tràn ẩn khi con chuột rời đi.

Cá nhân tôi không nghĩ nó có vẻ xấu, mã của tôi có thể sử dụng chuyển đổi để làm cho nó sạch hơn và có những lợi ích rõ ràng để tạo hiệu ứng này mà người dùng không hề hay biết. Vì vậy, đây có lẽ là câu trả lời khó nhất.

//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";
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.