iOS Safari - 如何禁用过度滚动但允许可滚动的div正常滚动?

Jef*_*eff 98 safari jquery mobile-website ipad ios

我正在开发一个基于iPad的网络应用程序,需要防止过度滚动,以免它看起来像网页.我目前正在使用它来冻结视口并禁用过度滚动:

document.body.addEventListener('touchmove',function(e){
      e.preventDefault();
  });
Run Code Online (Sandbox Code Playgroud)

这可以很好地禁用过度滚动,但我的应用程序有几个可滚动的div,上面的代码阻止它们滚动.

我只针对iOS 5及以上版本,所以我避免使用像iScroll这样的hacky解决方案.相反,我将这个CSS用于我的可滚动div:

.scrollable {
    -webkit-overflow-scrolling: touch;
    overflow-y:auto;
}
Run Code Online (Sandbox Code Playgroud)

这没有文档overscroll脚本,但不解决div滚动问题.

没有jQuery插件,有没有办法使用过度滚动修复但免除我的$('.scrollable')divs?

编辑:

我发现了一些不错的解决方案:

 // Disable overscroll / viewport moving on everything but scrollable divs
 $('body').on('touchmove', function (e) {
         if (!$('.scrollable').has($(e.target)).length) e.preventDefault();
 });
Run Code Online (Sandbox Code Playgroud)

滚动浏览div的开头或结尾时,视口仍会移动.我想找到一种方法来禁用它.

小智 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();
});
Run Code Online (Sandbox Code Playgroud)

请注意,如果您想在div没有溢出时阻止整页滚动,这将不起作用.要阻止它,请使用以下事件处理程序而不是上面的事件处理程序(根据此问题改编):

$('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();
    }
});
Run Code Online (Sandbox Code Playgroud)

  • 工作得很好 - 这肯定比直接针对`.scrollable`更好(这是我最初尝试解决这个问题).如果你是一个JavaScript菜鸟,并希望简单的代码在某个地方移除这些处理程序,这两行对我来说非常有用!`$(document).off('touchmove');`AND` $('body').off('touchmove touchstart','.strrollable');` (2认同)

Kub*_*luj 23

使用Tyler Dodge的优秀答案一直滞后于我的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();
});
Run Code Online (Sandbox Code Playgroud)

此外,添加以下CSS修复了一些渲染故障():

.scrollable {
    overflow: auto;
    overflow-x: hidden;
    -webkit-overflow-scrolling: touch;
}
.scrollable * {
    -webkit-transform: translate3d(0,0,0);
}
Run Code Online (Sandbox Code Playgroud)


Jon*_*nge 12

首先像往常一样阻止整个文档的默认操作:

$(document).bind('touchmove', function(e){
  e.preventDefault();           
});
Run Code Online (Sandbox Code Playgroud)

然后停止您的元素类传播到文档级别.这会阻止它到达上面的函数,因此不会启动e.preventDefault():

$('.scrollable').bind('touchmove', function(e){
  e.stopPropagation();
});
Run Code Online (Sandbox Code Playgroud)

这个系统似乎比在所有触摸动作上计算类更自然且更少密集.对动态生成的元素使用.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" />
Run Code Online (Sandbox Code Playgroud)


Dev*_*Joe 7

您是否可以在过度滚动禁用代码中添加更多逻辑,以确保有问题的目标元素不是您想要滚动的元素?像这样的东西:

document.body.addEventListener('touchmove',function(e){
     if(!$(e.target).hasClass("scrollable")) {
       e.preventDefault();
     }
 });
Run Code Online (Sandbox Code Playgroud)

  • 谢谢......似乎这个_should_工作,但事实并非如此.此外,它不应该是"可滚动"而不是".scrollable"(带点)? (3认同)
  • 如果使用jQuery,为什么会使用document.body.addEventListener?这是有原因的吗? (3认同)

Eli*_*sen 6

对此最好的解决方案是css/html:创建一个div来包装你的元素,如果你还没有它并将它设置为固定位置和溢出隐藏.可选,如果您希望它填满整个屏幕,除了整个屏幕之外,只需将高度和宽度设置为100%

#wrapper{
  height: 100%;
  width: 100%;
  position: fixed;
  overflow: hidden;
}
Run Code Online (Sandbox Code Playgroud)
<div id="wrapper">
  <p>All</p>
  <p>Your</p>
  <p>Elements</p>
</div>
Run Code Online (Sandbox Code Playgroud)


小智 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();
    }
});
Run Code Online (Sandbox Code Playgroud)