滚动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;
    });
});
  • 这将停止所有滚动,直到我仍希望在容器内仍可以滚动
  • 鼠标离开时也不会停用

有什么想法或更好的方法吗?


您将主体设置为从鼠标滚轮返回false,也许这是问题所在,我想您的容器在主体内部
Ricardo Binns

@ric_bfa是的,但是如何解决
RIK

改为设置您的class / id容器作为主体
Ricardo Binns

也许我应该澄清一下。当鼠标位于元素“ .scrollable”内时,身体的滚动能力应停用
RIK

4
没有所有CSS且没有JavaScript的方法吗?
查尔维

Answers:


165

更新2:我的解决方案基于完全禁用浏览器的本机滚动(当光标位于DIV内时),然后使用JavaScript手动滚动DIV(通过设置其.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,输出

因此,您可以手动设置滚动位置,然后阻止默认行为(即滚动DIV或整个网页)。

更新1:正如克里斯在下面的评论中指出的那样,在较新版本的jQuery中,增量信息嵌套在.originalEvent对象中,即jQuery不再在其自定义Event对象中公开它,我们必须从本机Event对象中检索它。 。


2
@RobinKnight不,看起来不是这样。这里是工作演示:jsfiddle.net/XNwbt/1并在这里与那些线演示中删除:jsfiddle.net/XNwbt/2
森那维达斯

1
在Firefox 10上,至少在Linux和Mac上,您的手动滚动是向后的。如果您制作了该产品-e.detail,则似乎可以正常工作,并已在Firefox(Mac,Linux),Safari(Mac)和Chromium(Linux)中进行了测试。
Anomie 2012年

1
@Anomie是的。Mozilla的.detail符号与.wheelData(Chrome / IE的)符号不符,因此我们必须手动将其反转。感谢您修正我的答案。
森那维达斯

8
我用了这个,谢谢!我需要添加以下内容:if( e.originalEvent ) e = e.originalEvent;
Nikso

2
@ŠimeVidas在软件中实现此功能后,我对此进行了调整。这不是很关键,但是可以为scroll属性添加一个健全性检查,以防止当元素没有滚动时死锁滚动。if ( $(this)[0].scrollHeight !== $(this).outerHeight() ) { //yourcode }仅在有滚动条时执行。
user0000001

30

如果您不关心与旧IE版本(<8)的兼容性,则可以制作一个自定义jQuery插件,然后在溢出元素上调用它。

该解决方案比ŠimeVidas提出的解决方案具有优势,因为它不会覆盖滚动行为-只是在适当的时候将其阻止。

$.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中可以完美运行。
2013年

1
谢谢!其他解决方案使滚动变得超级慢且
容易出错

@wojcikstefan,我在浏览器中收到此警告:[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

我认为有时可以取消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 2015年

6

在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有点错误。在Firefox中,“ DOMMouseScroll”事件没有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的顶部或底部时,第一个事件会进入主体。在osx中​​使用2手指触摸对chrome进行测试。
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ñakiBaz
Castillo

2

这是我在应用程序中使用过的解决方案。

我禁用了身体溢出功能,并将整个网站html放在了div容器中。网站容器溢出,因此用户可以按预期滚动页面。

然后,我创建了一个具有较高z索引的兄弟div(#Prevent),该索引涵盖了整个网站。由于#Prevent具有较高的z-index,因此它与网站容器重叠。当#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 2014年

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的情况下执行此操作。您可以将两个div的样式都设置为position: fixedoverflow-y: auto。您可能需要通过设置其中一个使其高于另一个z-index(如果它们重叠)。

这是CodePen上一个基本示例


0

这是用于防止在滚动特定div时父母滚动的有用插件,并且有很多选项可以使用。

在这里查看:

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

用法

通过JavaScript触发滚动锁定:

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

通过标记触发滚动锁定:

    <!-- 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更改为隐藏溢出。

我个人并不认为它看起来很糟糕,我的代码可以使用toggle使其更整洁,并且在用户不知情的情况下使这种效果成为可能有明显的好处。因此,这可能是最后的解决办法。

//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";
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.