我正在使用基于iPad的网络应用,并且需要防止过度滚动,以免它看起来像网页。我目前正在使用它来冻结视口并禁用过度滚动:
document.body.addEventListener('touchmove',function(e){
e.preventDefault();
});
这可以很好地禁用过度滚动但我的应用程序有几个可滚动的div, 上面的代码阻止它们滚动 。
我只针对iOS 5及以上版本,所以我避免使用像iScroll这样的hacky解决方案。相反,我将这个CSS用于我的可滚动div:
.scrollable {
-webkit-overflow-scrolling: touch;
overflow-y:auto;
}
这可以在没有文档过度滚动脚本的情况下工作,但不能解决div滚动问题。
如果没有jQuery插件, 有没有办法使用过度滚动修复但是免除我的$('。scrollable')div?
修改
我发现了一些不错的解决方案:
// Disable overscroll / viewport moving on everything but scrollable divs
$('body').on('touchmove', function (e) {
if (!$('.scrollable').has($(e.target)).length) e.preventDefault();
});
当您滚过div的开头或结尾时,视口仍会移动。我想找到一种方法来禁用它。
答案 0 :(得分:84)
当你滚过div的开头或结尾时,这解决了这个问题
var selScrollable = '.scrollable';
// Uses document because document will be topmost level in bubbling
$(document).on('touchmove',function(e){
e.preventDefault();
});
// Uses body because jQuery on events are called off of the element they are
// added to, so bubbling would not work if we used document instead.
$('body').on('touchstart', selScrollable, function(e) {
if (e.currentTarget.scrollTop === 0) {
e.currentTarget.scrollTop = 1;
} else if (e.currentTarget.scrollHeight === e.currentTarget.scrollTop + e.currentTarget.offsetHeight) {
e.currentTarget.scrollTop -= 1;
}
});
// Stops preventDefault from being called on document if it sees a scrollable div
$('body').on('touchmove', selScrollable, function(e) {
e.stopPropagation();
});
请注意,如果要在div没有溢出时阻止整页滚动,这将不起作用。要阻止它,请使用以下事件处理程序而不是上面的事件处理程序(改编自this question):
$('body').on('touchmove', selScrollable, function(e) {
// Only block default if internal div contents are large enough to scroll
// Warning: scrollHeight support is not universal. (https://stackoverflow.com/a/15033226/40352)
if($(this)[0].scrollHeight > $(this).innerHeight()) {
e.stopPropagation();
}
});
答案 1 :(得分:23)
使用Tyler Dodge的优秀answer保持在我的iPad上,所以我添加了一些限制代码,现在它非常流畅。滚动时有时会有一些最小的跳过。
// Uses document because document will be topmost level in bubbling
$(document).on('touchmove',function(e){
e.preventDefault();
});
var scrolling = false;
// Uses body because jquery on events are called off of the element they are
// added to, so bubbling would not work if we used document instead.
$('body').on('touchstart','.scrollable',function(e) {
// Only execute the below code once at a time
if (!scrolling) {
scrolling = true;
if (e.currentTarget.scrollTop === 0) {
e.currentTarget.scrollTop = 1;
} else if (e.currentTarget.scrollHeight === e.currentTarget.scrollTop + e.currentTarget.offsetHeight) {
e.currentTarget.scrollTop -= 1;
}
scrolling = false;
}
});
// Prevents preventDefault from being called on document if it sees a scrollable div
$('body').on('touchmove','.scrollable',function(e) {
e.stopPropagation();
});
此外,添加以下CSS修复了一些渲染故障(source):
.scrollable {
overflow: auto;
overflow-x: hidden;
-webkit-overflow-scrolling: touch;
}
.scrollable * {
-webkit-transform: translate3d(0,0,0);
}
答案 2 :(得分:12)
首先像往常一样阻止整个文档的默认操作:
$(document).bind('touchmove', function(e){
e.preventDefault();
});
然后阻止您的元素类传播到文档级别。这会阻止它到达上面的函数,因此不会启动e.preventDefault():
$('.scrollable').bind('touchmove', function(e){
e.stopPropagation();
});
这个系统似乎比在所有触摸动作上计算课程更自然,更少强度。对动态生成的元素使用.on()而不是.bind()。
还要考虑这些元标记,以防止在使用可滚动div时发生不幸事件:
<meta content='True' name='HandheldFriendly' />
<meta content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0' name='viewport' />
<meta name="viewport" content="width=device-width" />
答案 3 :(得分:7)
您是否可以在过度滚动禁用代码中添加更多逻辑,以确保有问题的目标元素不是您想要滚动的元素?像这样:
document.body.addEventListener('touchmove',function(e){
if(!$(e.target).hasClass("scrollable")) {
e.preventDefault();
}
});
答案 4 :(得分:6)
最佳解决方案是css / html: 如果你还没有把你的元素包装成一个div 并将其设置为固定位置和溢出隐藏。可选,如果您希望它填满整个屏幕并且只有整个屏幕,则将高度和宽度设置为100%
<div id="wrapper">
<p>All</p>
<p>Your</p>
<p>Elements</p>
</div>
@Entity
class SomeData {
private Long dataId;
private String name;
private Integer[] data;
}
答案 5 :(得分:5)
在尝试向上滚动时检查可滚动元素是否已滚动到顶部,或者在尝试向下滚动时检查是否已滚动到底部,然后阻止默认操作停止整个页面移动。
var touchStartEvent;
$('.scrollable').on({
touchstart: function(e) {
touchStartEvent = e;
},
touchmove: function(e) {
if ((e.originalEvent.pageY > touchStartEvent.originalEvent.pageY && this.scrollTop == 0) ||
(e.originalEvent.pageY < touchStartEvent.originalEvent.pageY && this.scrollTop + this.offsetHeight >= this.scrollHeight))
e.preventDefault();
}
});
答案 6 :(得分:4)
我正在寻找一种方法来阻止所有正文滚动,当有一个带有可滚动区域的弹出窗口(一个“购物车”弹出窗口,其中有一个可滚动的推车视图)。
我写了一个更优雅的解决方案,使用最小的javascript来切换你的身体上的类“noscroll”,当你有一个你想要滚动的弹出窗口或div(而不是“过度滚动”整个页面主体)。
当桌面浏览器观察到溢出时:隐藏 - iOS似乎忽略了这一点,除非你将位置设置为固定...导致整个页面都是一个奇怪的宽度,所以你必须手动设置位置和宽度。使用这个css:
.noscroll {
overflow: hidden;
position: fixed;
top: 0;
left: 0;
width: 100%;
}
和这个jquery:
/* fade in/out cart popup, add/remove .noscroll from body */
$('a.cart').click(function() {
$('nav > ul.cart').fadeToggle(100, 'linear');
if ($('nav > ul.cart').is(":visible")) {
$('body').toggleClass('noscroll');
} else {
$('body').removeClass('noscroll');
}
});
/* close all popup menus when you click the page... */
$('body').click(function () {
$('nav > ul').fadeOut(100, 'linear');
$('body').removeClass('noscroll');
});
/* ... but prevent clicks in the popup from closing the popup */
$('nav > ul').click(function(event){
event.stopPropagation();
});
答案 7 :(得分:3)
我在没有jquery的情况下进行了一些解决方法。不完美但工作正常(特别是如果你有一个scoll-y中的scroll-x)https://github.com/pinadesign/overscroll/
免费参与并改进
答案 8 :(得分:1)
此解决方案并不要求您在所有可滚动的div上放置可滚动的类,因此更为通用。所有元素都是允许滚动的,这些元素是INPUT元素contenteditables和overflow scroll或autos的子节点。
我使用自定义选择器,我还在元素中缓存检查结果以提高性能。无需每次都检查相同的元素。这可能有一些问题,只是刚刚写完但我想分享。
$.expr[':'].scrollable = function(obj) {
var $el = $(obj);
var tagName = $el.prop("tagName");
return (tagName !== 'BODY' && tagName !== 'HTML') && (tagName === 'INPUT' || $el.is("[contentEditable='true']") || $el.css("overflow").match(/auto|scroll/));
};
function preventBodyScroll() {
function isScrollAllowed($target) {
if ($target.data("isScrollAllowed") !== undefined) {
return $target.data("isScrollAllowed");
}
var scrollAllowed = $target.closest(":scrollable").length > 0;
$target.data("isScrollAllowed",scrollAllowed);
return scrollAllowed;
}
$('body').bind('touchmove', function (ev) {
if (!isScrollAllowed($(ev.target))) {
ev.preventDefault();
}
});
}
答案 9 :(得分:1)
禁用所有&#34; touchmove&#34;事件似乎是一个好主意,只要您在页面上需要其他可滚动元素就会导致问题。最重要的是,如果你只是禁用&#34; touchmove&#34;某些元素上的事件(例如,如果您希望页面不可滚动,则为正文),只要在其他任何地方启用它,IOS就会在URL栏切换时在Chrome中产生不可阻挡的传播。
虽然我无法解释这种行为,但似乎唯一的防止方法似乎是将身体的位置设置为fixed
。唯一的问题是你将丢失文档的位置 - 例如,这在模态中尤其令人讨厌。解决它的一种方法是使用这些简单的VanillaJS函数:
function disableDocumentScrolling() {
if (document.documentElement.style.position != 'fixed') {
// Get the top vertical offset.
var topVerticalOffset = (typeof window.pageYOffset != 'undefined') ?
window.pageYOffset : (document.documentElement.scrollTop ?
document.documentElement.scrollTop : 0);
// Set the document to fixed position (this is the only way around IOS' overscroll "feature").
document.documentElement.style.position = 'fixed';
// Set back the offset position by user negative margin on the fixed document.
document.documentElement.style.marginTop = '-' + topVerticalOffset + 'px';
}
}
function enableDocumentScrolling() {
if (document.documentElement.style.position == 'fixed') {
// Remove the fixed position on the document.
document.documentElement.style.position = null;
// Calculate back the original position of the non-fixed document.
var scrollPosition = -1 * parseFloat(document.documentElement.style.marginTop);
// Remove fixed document negative margin.
document.documentElement.style.marginTop = null;
// Scroll to the original position of the non-fixed document.
window.scrollTo(0, scrollPosition);
}
}
使用此解决方案,您可以使用简单的CSS(例如overflow: scroll;
)获得固定文档,并且页面中的任何其他元素都会溢出。不需要特殊课程或其他任何东西。
答案 10 :(得分:0)
这是一个zepto兼容的解决方案
if (!$(e.target).hasClass('scrollable') && !$(e.target).closest('.scrollable').length > 0) {
console.log('prevented scroll');
e.preventDefault();
window.scroll(0,0);
return false;
}
答案 11 :(得分:0)
这个适用于我(普通的javascript)
var fixScroll = function (className, border) { // className = class of scrollElement(s), border: borderTop + borderBottom, due to offsetHeight
var reg = new RegExp(className,"i"); var off = +border + 1;
function _testClass(e) { var o = e.target; while (!reg.test(o.className)) if (!o || o==document) return false; else o = o.parentNode; return o;}
document.ontouchmove = function(e) { var o = _testClass(e); if (o) { e.stopPropagation(); if (o.scrollTop == 0) { o.scrollTop += 1; e.preventDefault();}}}
document.ontouchstart = function(e) { var o = _testClass(e); if (o && o.scrollHeight >= o.scrollTop + o.offsetHeight - off) o.scrollTop -= off;}
}
fixScroll("fixscroll",2); // assuming I have a 1px border in my DIV
HTML:
<div class="fixscroll" style="border:1px gray solid">content</div>
答案 12 :(得分:0)
试试这个,它将完美无缺。
$('body.overflow-hidden').delegate('#skrollr-body','touchmove',function(e){
e.preventDefault();
console.log('Stop skrollrbody');
}).delegate('.mfp-auto-cursor .mfp-content','touchmove',function(e){
e.stopPropagation();
console.log('Scroll scroll');
});
答案 13 :(得分:0)
我的运气很简单:
body {
height: 100vh;
}
它可以很好地禁用弹出窗口或菜单的过度滚动,并且它不会强制浏览器栏显示为使用position:fixed时。但是 - 您需要在设置固定高度之前保存滚动位置,并在隐藏弹出窗口时恢复它,否则,浏览器将滚动到顶部。