滚动 DIV 元素时如何防止页面滚动?
Posted
技术标签:
【中文标题】滚动 DIV 元素时如何防止页面滚动?【英文标题】:How to prevent page scrolling when scrolling a DIV element? 【发布时间】: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 来做到这一点吗? 【参考方案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 < 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/
【讨论】:
我刚刚发布了一个实现上述代码的 NPM 库:npmjs.com/package/dontscrollthebody【参考方案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: fixed
和 overflow-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();
在子元素上。
【讨论】:
以上是关于滚动 DIV 元素时如何防止页面滚动?的主要内容,如果未能解决你的问题,请参考以下文章