จะป้องกันการเลื่อนหน้าเมื่อเลื่อนองค์ประกอบ DIV ได้อย่างไร


94

ฉันได้ตรวจสอบและทดสอบฟังก์ชันต่างๆเพื่อป้องกันความสามารถของร่างกายในการเลื่อนในขณะที่อยู่ใน div และได้รวมฟังก์ชันที่ควรใช้งานได้

$('.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;
    });
});
  • นี่เป็นการหยุดการเลื่อนทั้งหมดโดยที่ฉันต้องการให้การเลื่อนยังคงเป็นไปได้ภายในคอนเทนเนอร์
  • นอกจากนี้ยังไม่ปิดการใช้งานเมื่อปล่อยเมาส์

มีความคิดหรือวิธีการที่ดีกว่านี้หรือไม่?


คุณตั้งค่าร่างกายเป็น return false จาก mousewheel บางทีนี่อาจเป็นปัญหาฉันคิดว่าคอนเทนเนอร์ของคุณอยู่ในร่างกายถูกต้อง
Ricardo Binns

@ric_bfa ใช่ แต่จะแก้ไขอย่างไร
RIK

แทน body ตั้งคลาส / id container ของคุณ
Ricardo Binns

บางทีฉันควรชี้แจง เมื่อเมาส์อยู่ในองค์ประกอบ ".scrollable" ความสามารถในการเลื่อนของร่างกายควรถูกปิดใช้งาน
RIK

4
ไม่มีวิธีทำเช่นนี้กับ CSS ทั้งหมดและไม่มี JavaScript หรือไม่?
chharvey

คำตอบ:


167

อัปเดต 2:โซลูชันของฉันขึ้นอยู่กับการปิดใช้งานการเลื่อนแบบเนทีฟของเบราว์เซอร์ทั้งหมด (เมื่อเคอร์เซอร์อยู่ภายใน DIV) จากนั้นเลื่อน DIV ด้วย JavaScript ด้วยตนเอง (โดยการตั้งค่า.scrollTopคุณสมบัติ) อีกทางเลือกหนึ่งและแนวทางที่ดีกว่าของ IMO คือการเลือกปิดการใช้งานการเลื่อนของเบราว์เซอร์เท่านั้นเพื่อป้องกันการเลื่อนหน้า แต่ไม่ใช่การเลื่อน DIV ตรวจสอบคำตอบของ Rudie ด้านล่างซึ่งแสดงให้เห็นถึงโซลูชันนี้


ได้แล้ว:

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

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

การสาธิตสด: https://jsbin.com/howojuq/edit?js,output

ดังนั้นคุณจึงตั้งค่าตำแหน่งการเลื่อนด้วยตนเองจากนั้นเพียงแค่ป้องกันพฤติกรรมเริ่มต้น (ซึ่งจะเป็นการเลื่อน DIV หรือเว็บเพจทั้งหมด)

อัปเดต 1: ดังที่ Chris ระบุไว้ในความคิดเห็นด้านล่างใน jQuery เวอร์ชันที่ใหม่กว่าข้อมูลเดลต้าจะซ้อนอยู่ภายใน.originalEventออบเจ็กต์กล่าวคือ jQuery จะไม่เปิดเผยในวัตถุเหตุการณ์ที่กำหนดเองอีกต่อไปและเราต้องดึงข้อมูลจากวัตถุเหตุการณ์ดั้งเดิมแทน .


2
@RobinKnight ไม่ดูเหมือนจะไม่เป็นเช่นนั้น นี่คือการสาธิตที่ใช้งานได้: jsfiddle.net/XNwbt/1และนี่คือการสาธิตโดยลบบรรทัดเหล่านั้นออก: jsfiddle.net/XNwbt/2
Šime Vidas

1
การเลื่อนด้วยตนเองของคุณเป็นการย้อนกลับบน Firefox 10 อย่างน้อยบน Linux และ Mac ดูเหมือนว่าจะทำงานได้อย่างถูกต้องหากคุณทำเช่นนั้น-e.detailทดสอบใน Firefox (Mac, Linux), Safari (Mac) และ Chromium (Linux)
Anomie

1
@Anomie ที่ถูกต้อง. สัญลักษณ์ของ Mozilla .detailไม่ตรงกับสัญลักษณ์ของ.wheelData(ซึ่งก็คือสิ่งที่ Chrome / IE มี) ดังนั้นเราจึงต้องพลิกกลับด้วยตนเอง ขอบคุณสำหรับการแก้ไขคำตอบของฉัน
Šime Vidas

8
ฉันใช้สิ่งนี้ขอบคุณ! ฉันต้องการเพิ่มสิ่งนี้:if( e.originalEvent ) e = e.originalEvent;
Nikso

2
@ ŠimeVidasฉันได้ทำการปรับเปลี่ยนหลังจากใช้งานในซอฟต์แวร์แล้ว ไม่สำคัญ แต่อาจเพิ่มการตรวจสอบสติสัมปชัญญะสำหรับคุณสมบัติการเลื่อนเพื่อป้องกันการเลื่อนการหยุดชะงักเมื่อองค์ประกอบไม่มีการเลื่อน if ( $(this)[0].scrollHeight !== $(this).outerHeight() ) { //yourcode }จะดำเนินการก็ต่อเมื่อมีแถบเลื่อน
user0000001

30

หากคุณไม่สนใจเกี่ยวกับความเข้ากันได้กับ IE เวอร์ชันเก่า (<8) คุณสามารถสร้างปลั๊กอิน jQuery แบบกำหนดเองจากนั้นเรียกมันในองค์ประกอบที่ล้น

โซลูชันนี้มีข้อได้เปรียบเหนือŠime Vidas ที่เสนอเนื่องจากไม่ได้เขียนทับพฤติกรรมการเลื่อน - เพียงแค่บล็อกตามความเหมาะสม

$.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
ขอบคุณ! ฉันคิดว่ามันเหมาะสมกว่าที่จะไม่เขียนทับพฤติกรรมเริ่มต้น ในการรองรับข้ามเบราว์เซอร์สามารถใช้ปลั๊กอินjQuery Mouse Wheel
Meettya

น่าเสียดายที่สิ่งนี้ดูเหมือนจะผิดพลาดใน Chrome 52 (OSX 10.10) ทำงานได้อย่างสมบูรณ์แบบใน Safari แม้ว่า
หนาวจัด

1
ขอขอบคุณ! วิธีแก้ปัญหาอื่น ๆ ทำให้การเลื่อนช้ามากและมีปัญหา
agrublev

@wojcikstefan ฉันได้รับคำเตือนนี้ใน 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.
ไซ

21

ฉันคิดว่าเป็นไปได้ที่จะยกเลิกกิจกรรม 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();
});

ภายในตัวจัดการเหตุการณ์คุณจะต้องรู้:

  • ทิศทางการเลื่อน
    d = e.originalEvent.deltaY, dir = d < 0 ? 'up' : 'down' เนื่องจากจำนวนบวกหมายถึงการเลื่อนลง
  • ตำแหน่งเลื่อน
    scrollTopสำหรับด้านบนscrollHeight - scrollTop - offsetHeightสำหรับด้านล่าง

ถ้าคุณคือ

  • เลื่อนขึ้นและtop = 0หรือ
  • เลื่อนลงและbottom = 0,

ยกเลิกกิจกรรม: e.preventDefault()(และอาจจะด้วยซ้ำe.stopPropagation())

ฉันคิดว่าจะดีกว่าถ้าไม่ลบล้างพฤติกรรมการเลื่อนของเบราว์เซอร์ ยกเลิกเมื่อทำได้เท่านั้น

มันน่าจะไม่ใช่ xbrowser ที่สมบูรณ์แบบ แต่ก็ไม่ยากมาก บางทีทิศทางการเลื่อนคู่ของ Mac อาจยุ่งยากแม้ว่า ...


2
ฉันจะใช้ฟังก์ชันเดียวกันสำหรับอุปกรณ์ (แท็บเล็ต / โทรศัพท์) ได้อย่างไรฉันเดาว่า touchmove เป็นเหตุการณ์ที่มีผลผูกพัน
ViVekH

13

ใช้คุณสมบัติ CSS ด้านล่าง overscroll-behavior: contain;กับองค์ประกอบลูก


โซลูชันอื่น ๆ ทั้งหมดมีการใช้งานมากเกินไปเมื่อเทียบกับกฎ CSS ดั้งเดิมนี้ ทำได้ดีมาก
TechWisdom

3

ดูว่าสิ่งนี้ช่วยคุณได้หรือไม่:

สาธิต: jsfiddle

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

แก้ไข:

ลองสิ่งนี้:

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

องค์ประกอบของคุณแยกจากกันในขณะที่องค์ประกอบของฉันจะอยู่ในอีกองค์ประกอบหนึ่ง
RIK

3

วิธีแก้ปัญหาที่แฮ็กน้อยกว่าในความคิดของฉันคือการตั้งค่าโอเวอร์โฟลว์ที่ซ่อนอยู่ในร่างกายเมื่อคุณวางเมาส์เหนือ div ที่เลื่อนได้ วิธีนี้จะป้องกันไม่ให้ร่างกายเลื่อน แต่จะเกิดผล "กระโดด" ที่ไม่ต้องการ วิธีแก้ปัญหาต่อไปนี้สามารถแก้ไขได้:

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

คุณสามารถทำให้โค้ดของคุณฉลาดขึ้นได้หากจำเป็น ตัวอย่างเช่นคุณสามารถทดสอบว่าร่างกายมีช่องว่างภายในอยู่แล้วหรือไม่และถ้าใช่ให้เพิ่มช่องว่างภายในใหม่เข้าไป


3

ในโซลูชันข้างต้นมีข้อผิดพลาดเล็กน้อยเกี่ยวกับ Firefox ในเหตุการณ์ "DOMMouseScroll" ของ Firefox ไม่มีคุณสมบัติ e.detail หากต้องการรับคุณสมบัตินี้คุณควรเขียน "e.originalEvent.detail" ต่อไปนี้

นี่คือโซลูชันที่ใช้งานได้สำหรับ 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;
};

วิธีนี้ใช้ได้ดียกเว้นเมื่อคุณชนที่ด้านบนหรือด้านล่างของ div เหตุการณ์แรกจะเข้าสู่ร่างกาย การทดสอบด้วยการแตะ 2 นิ้วบน chrome ใน osx
johnb003

3

นี่เป็นวิธีง่ายๆที่ไม่มี jQuery ซึ่งไม่ทำลายการเลื่อนแบบเนทีฟของเบราว์เซอร์ (นี่คือ: ไม่มีการเลื่อนแบบเทียม / น่าเกลียด)

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

การสาธิตสด: http://jsfiddle.net/ibcaliax/bwmzfmq7/4/


ฉันเพิ่งเผยแพร่ห้องสมุด NPM โดยใช้รหัสข้างต้น: npmjs.com/package/dontscrollthebody
Iñaki Baz Castillo

2

นี่คือวิธีแก้ปัญหาของฉันที่ฉันใช้ในแอปพลิเคชัน

ฉันปิดการใช้งาน body overflow และวาง html ของเว็บไซต์ทั้งหมดไว้ในคอนเทนเนอร์ div คอนเทนเนอร์ของเว็บไซต์มีมากเกินไปดังนั้นผู้ใช้อาจเลื่อนหน้าเว็บได้ตามที่คาดไว้

จากนั้นฉันสร้าง div พี่น้อง (#Prevent) ด้วยดัชนี z ที่สูงกว่าซึ่งครอบคลุมทั้งเว็บไซต์ เนื่องจาก #Prevent มีดัชนี z สูงกว่าจึงซ้อนทับคอนเทนเนอร์ของเว็บไซต์ เมื่อมองเห็น #Prevent เมาส์จะไม่วางเมาส์เหนือคอนเทนเนอร์ของเว็บไซต์อีกต่อไปจึงไม่สามารถเลื่อนได้

แน่นอนคุณอาจวาง div อื่นเช่นโมดอลของคุณที่มีดัชนี z สูงกว่า #Prevent ในมาร์กอัป วิธีนี้ช่วยให้คุณสร้างหน้าต่างป๊อปอัปที่ไม่ประสบปัญหาการเลื่อน

วิธีนี้ดีกว่าเพราะไม่ซ่อนแถบเลื่อน (มีผลต่อการกระโดด) ไม่ต้องการผู้ฟังเหตุการณ์และง่ายต่อการนำไปใช้ มันทำงานได้ในทุกเบราว์เซอร์แม้ว่าคุณจะต้องเล่น IE7 & 8 (ขึ้นอยู่กับรหัสเฉพาะของคุณ)

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

ปิด / เปิดการเลื่อน

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

2
โปรดพิจารณารวมข้อมูลบางอย่างเกี่ยวกับคำตอบของคุณแทนที่จะโพสต์รหัส เราพยายามให้ไม่ใช่แค่ 'การแก้ไข' แต่ช่วยให้ผู้คนเรียนรู้ คุณควรอธิบายสิ่งที่ผิดพลาดในรหัสเดิมสิ่งที่คุณทำแตกต่างออกไปและเหตุใดการเปลี่ยนแปลงของคุณจึงได้ผล
Andrew Barber

1

ฉันต้องการเพิ่มเหตุการณ์นี้ในหลายองค์ประกอบที่อาจมีแถบเลื่อน ในกรณีที่ไม่มีแถบเลื่อนแสดงว่าแถบเลื่อนหลักไม่ทำงานเท่าที่ควร ดังนั้นฉันจึงทำการเปลี่ยนแปลงเล็กน้อยเป็นรหัส @ Šimeดังนี้:

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

ตอนนี้มีเพียงองค์ประกอบที่มีแถบเลื่อนเท่านั้นที่จะป้องกันไม่ให้การเลื่อนหลักหยุดลง


0

คำตอบของ Vidas เวอร์ชัน JavaScript แท้el$คือโหนด DOM ของเครื่องบินที่คุณกำลังเลื่อน

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

ตรวจสอบให้แน่ใจว่าคุณไม่ติดซ้ำหลายครั้ง! นี่คือตัวอย่าง

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

คำเตือนฟังก์ชันจะต้องมีค่าคงที่หากคุณเริ่มต้นฟังก์ชันใหม่ทุกครั้งก่อนที่จะติดตั้งเช่น จะไม่ทำงานvar func = function (...)removeEventListener


0

คุณสามารถทำได้โดยไม่ต้องใช้ JavaScript คุณสามารถตั้งค่ารูปแบบใน divs ทั้งสองและposition: fixed overflow-y: autoคุณอาจต้องทำให้หนึ่งในนั้นสูงกว่าอีกอันหนึ่งโดยการตั้งค่าz-index (หากทับซ้อนกัน)

นี่คือตัวอย่างที่พื้นฐานเกี่ยวกับ CodePen


0

นี่คือปลั๊กอินที่มีประโยชน์ในการป้องกันการเลื่อนของผู้ปกครองในขณะที่เลื่อน div เฉพาะและมีตัวเลือกมากมายให้เล่นด้วย

ตรวจสอบได้ที่นี่:

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

การใช้งาน

ทริกเกอร์ Scroll Lock ผ่าน JavaScript:

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

ทริกเกอร์ Scroll Lock ผ่านมาร์กอัป:

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

ดูการสาธิต


0

เพียงแค่เสนอสิ่งนี้เป็นทางออกที่เป็นไปได้หากคุณไม่คิดว่าผู้ใช้จะมีประสบการณ์เชิงลบกับการเปลี่ยนแปลงที่เห็นได้ชัด ฉันเพียงแค่เปลี่ยนระดับของการล้นของร่างกายเป็นซ่อนเมื่อเมาส์อยู่เหนือ div เป้าหมาย จากนั้นฉันก็เปลี่ยน div ของร่างกายเป็นโอเวอร์โฟลว์ที่ซ่อนอยู่เมื่อเมาส์ออกไป

โดยส่วนตัวแล้วฉันไม่คิดว่ามันดูแย่รหัสของฉันสามารถใช้สลับเพื่อทำให้มันสะอาดขึ้นและมีประโยชน์ที่ชัดเจนสำหรับการทำให้เอฟเฟกต์นี้เป็นไปได้โดยที่ผู้ใช้ไม่รู้ตัว นี่อาจเป็นคำตอบสุดท้ายของแฮ็ก

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.