在 iOS 上防止溢出/橡皮筋滚动

Prevent overflow / rubberband scrolling on iOS

关于 overflow/rubberband 在 SO 上滚动的主题已经有多个问题但是

  1. none 提供了适用于 iOS 9.3.2
  2. 上所有情况的解决方案
  3. none 其中提供了有关问题本身的广泛而完整的信息

这就是我创建此 post 知识体系的原因。


问题:

在任何其他 post 中从未提及的事情是 iOS 溢出滚动实际上是一个两部分的行为。

1。使用 overflow: auto/scroll

溢出滚动内容

这是具有 -webkit-overflow-scrolling: touch 的元素的众所周知且最需要的行为,其中 continuous/momentum 滚动行为经过元素容器以平滑地减慢滚动内容。

当您以足够高的动量滚动元素的内容以使动量滚动超过滚动内容的长度时,就会发生这种情况。

通过这种行为,element.scrollTop 属性 相应地根据元素滚动位置发生变化,并且小于 0 或大于最大滚动(element.scrollHeight - element.offsetHeight)。

2。 <body>

的溢出滚动

如果您尝试将任何已经在其 minimum/maximum 滚动位置的元素滚动到比该位置更远的位置(顶部向上的元素或底部向下的元素),则会出现此行为。然后滚动似乎 "bubble up" 到 <body> 标签,整个视口都滚动了。

与上面相反,element.scrollTop 属性 没有改变,但是 document.body.scrollTop 改变了。

焦点锁定和行为切换(1.5秒延迟)

这个上下文中最令人恼火的是,上述两种类型之间的切换不会立即切换。

输入两者之一后,您无法将焦点切换到任何其他元素(可滚动元素、按钮、链接等),因此滚动行为也不会改变。

For instance: if you scroll a element already at its top position upwards you enter overflow scrolling type 2 and the most natural reaction for a user is to then try to scroll back down. Because the focus is locked to the body scroll instead of going to overflow scrolling type 1 it stays in type 2 and the whole body is scrolled downwards. The typical user then starts to arbitrarily starts to scroll up and down frequently without ever breaking out of type 2.

焦点的切换和滚动行为的改变只能发生在溢出动画结束并且元素静止不动之后(甚至比那长一点[大约 0.5s])。

thus going back to the previous example the correct reaction of the user would be to stop touching the screen for around 1s - 1.5s and then try to scroll downwards again.

解决方法:

类型 1:

防止元素本身溢出滚动的最基本解决方案是防止默认触摸事件。

document.body.addEventListener('touchmove', function(e) { 
    e.preventDefault(); 
});

然而,此方法会禁用浏览器的本机动量滚动,因此不适用于大多数应用程序。然而,通过一些改进(只有在顶部向上滚动或底部向下滚动时才能防止......)这种方法解决了大多数问题。 this SO post.

中可以找到许多可能的实现

类型 2:

然而,上面描述的方法并不能阻止 body 上的溢出滚动。

一个看似合理的可能解决方案是防止元素位于其顶部或底部位置,如 mentioned question 上描述的最佳解决方案。

anElement.addEventListener('touchstart', function( event ){
    if( this.scrollTop === 0 ) {
        this.scrollTop += 1;
    } else if( this.scrollTop + this.offsetHeight >= this.scrollHeight ) {
        this.scrollTop -= 1;
    }
}

然而,这在 iOS 9.3.2.

上并不可靠

但有效的方法 是在 <body> 元素上设置 position: fixed 以防止主体移动。 但是请注意,这仍然不能完全阻止 type 2 发生,这就是为什么有时你不能 scroll/focus 任何元素,因为在后台 type2它的焦点锁定仍在发生(再次,在您停止触摸屏幕片刻后它再次按预期工作)。

虽然这还远不是最佳解决方案,但它似乎是我们目前所能获得的最佳解决方案。

编辑: 请注意,我不确定将 position: fixed 放在 <body> 元素上是否安全。为了跟踪我创建的可能的问题 。显然,最好创建一个包装元素作为 body 的子元素并将该元素设置为 position: fixed 以避免缩放问题。


编辑 2:正解

脚本 iNoBounce 创造奇迹。只需将其加载到页面,即可体验无弹跳的 Web 应用程序。到目前为止,我还没有发现这个解决方案有任何问题。

一个不错的、简单的原生解决方案。不需要 Javascript。 只需将溢出滚动添加到您的 children。为您的应用提供漂亮的原生体验 'touch'

body{
    position: fixed;
}

我发现这个问题仍然相关,所以...

注意在 body 上使用 position: fixed。它可能会出现奇怪的滚动冻结错误 - 实际上它仍然会 "rubberband" 但你不会看到它。

参见:Div scrolling freezes sometimes if I use -webkit-overflow-scrolling