【问题标题】:How to prevent page scrolling when scrolling a DIV element?滚动 DIV 元素时如何防止页面滚动?
【发布时间】:2011-11-27 20:45:23
【问题描述】:

我已经审查并测试了各种防止身体在 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;
    });
});
  • 这将停止所有滚动,因为我希望在容器内仍然可以滚动
  • 这也不会在鼠标离开时停用

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

【问题讨论】:

  • 你将 body 设置为从鼠标滚轮返回 false,也许这就是问题所在,我想你的容器在 body 里面吧
  • @ric_bfa 是的,但是如何解决它
  • 代替正文,设置您的类/ID 容器
  • 也许我应该澄清一下。当鼠标在元素“.scrollable”内时,身体的滚动能力应该被禁用
  • 难道没有办法用所有的 CSS 而不用 JavaScript 来做到这一点吗?

标签: jquery mousewheel


【解决方案1】:

更新 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,output

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

更新 1: 正如 Chris 在下面的 cmets 中指出的,在较新版本的 jQuery 中,增量信息嵌套在 .originalEvent 对象中,即 jQuery 不会在其自定义事件对象中公开它我们必须从原生 Event 对象中检索它。

【讨论】:

  • @RobinKnight 不,似乎并非如此。这是工作演示:jsfiddle.net/XNwbt/1,这是删除了这些行的演示:jsfiddle.net/XNwbt/2
  • 您的手动滚动在 Firefox 10 上是向后的,至少在 Linux 和 Mac 上是这样。如果您在 Firefox(Mac、Linux)、Safari(Mac)和 Chromium(Linux)中测试过 -e.detail,似乎可以正常工作。
  • @Anomie 没错。 Mozilla 的.detail 的符号与.wheelData 的符号不对应(这是Chrome/IE 的),所以我们必须手动反转它。感谢您修复我的答案。
  • 我用过这个,谢谢!我需要添加这个:if( e.originalEvent ) e = e.originalEvent;
  • @ŠimeVidas 在我的软件中实现它后,我对此进行了调整。不重要,但可能会为滚动属性添加健全性检查,以防止在元素没有滚动时出现死锁滚动。 if ( $(this)[0].scrollHeight !== $(this).outerHeight() ) { //yourcode } 只有在有滚动条时才会执行。
【解决方案2】:

如果您不关心与旧 IE 版本 (

与 Š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();

【讨论】:

  • 谢谢!我认为不覆盖默认行为更合适。要跨浏览器支持,可以使用jQuery Mouse Wheel 插件。
  • 不幸的是,这在 Chrome 52 (OSX 10.10) 中似乎有问题。不过,在 Safari 中完美运行。
  • 谢谢!其他解决方案使滚动变得超级缓慢和错误
  • @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.
【解决方案3】:

我认为有时可以取消鼠标滚动事件: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 &lt; 0 ? 'up' : 'down' 因为正数意味着向下滚动
  • 滚动位置
    scrollTop 为顶部,scrollHeight - scrollTop - offsetHeight 为底部

如果你是

  • 向上滚动,然后top = 0,或
  • 向下滚动,然后bottom = 0

取消活动:e.preventDefault()(甚至可能是e.stopPropagation())。

我认为最好不要覆盖浏览器的滚动行为。仅在适用时取消。

这很可能不是完美的 xbrowser,但也不是很难。不过,也许 Mac 的双滚动方向很棘手......

【讨论】:

  • 我如何为设备(平板电脑/手机)实现相同的功能,我猜 touchmove 是绑定事件
【解决方案4】:

使用下面的 CSS 属性 overscroll-behavior: contain; 到子元素

【讨论】:

  • 与本机 CSS 规则相比,所有其他解决方案都太过分了。做得很好。
  • safari 或 ios 不支持此功能。
【解决方案5】:

看看这对你有没有帮助:

演示: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;
           });
       }
    });

【讨论】:

  • 你的元素是分开的,而我的一个包含在另一个中。
【解决方案6】:

在我看来,当您将鼠标悬停在可滚动的 div 上时,将溢出设置隐藏在 body 上。这将阻止身体滚动,但会出现不需要的“跳跃”效果。以下解决方案可以解决这个问题:

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

如果需要,您可以使您的代码更智能。例如,您可以测试主体是否已经有填充,如果是,则向其中添加新的填充。

【讨论】:

    【解决方案7】:

    在上面的解决方案中,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 进行测试。
    【解决方案8】:

    这里是一个没有 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/

    【讨论】:

    【解决方案9】:

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

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

    然后我创建了一个同级 div (#Prevent),它具有更高的 z-index 以覆盖整个网站。由于#Prevent 具有更高的 z-index,因此它与网站容器重叠。当#Prevent 可见时,鼠标不再悬停在网站容器上,因此无法滚动。

    您当然可以在标记中放置另一个 div,例如您的模态框,其 z-index 高于 #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
    

    【讨论】:

    • 请考虑包含一些关于您的答案的信息,而不是简单地发布代码。我们试图提供的不仅仅是“修复”,而是帮助人们学习。您应该解释原始代码中的问题、您所做的不同之处以及您的更改为何有效。
    【解决方案10】:

    我需要将此事件添加到可能具有滚动条的多个元素中。对于不存在滚动条的情况,主滚动条无法正常工作。所以我对@Š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();
        }       
    });
    

    现在,只有带有滚动条的元素会阻止主滚动开始停止。

    【讨论】:

      【解决方案11】:

      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 将不起作用。

      【讨论】:

        【解决方案12】:

        您可以在没有 JavaScript 的情况下做到这一点。您可以将两个 div 的样式设置为 position: fixedoverflow-y: auto。您可能需要通过设置z-index(如果它们重叠)使其中一个高于另一个。

        这里是a basic example on CodePen

        【讨论】:

          【解决方案13】:

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

          在这里查看:

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

          用法

          通过 JavaScript 触发 Scroll Lock:

          $('#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>
          

          查看Demo

          【讨论】:

            【解决方案14】:

            你只需要

            e.preventDefault();
            

            在子元素上。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-12-29
              • 2013-04-25
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多