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

SO上的溢出/橡皮圈滚动的话题已经有多个问题了

  1. 他们都没有提供适用于iOS 9.3.2的所有情况的解决scheme
  2. 他们都没有提供关于问题本身的广泛和完整的信息

这就是为什么我把这篇文章作为一个知识体。


问题:

在其他文章中从未提及过的事情是,iOS溢出滚动实际上是两部分行为。

1.溢出的内容溢出滚动overflow: auto/scroll

这是使用-webkit-overflow-scrolling: touch的元素的通常已知且最常见的行为,其中连续/动量滚动行为越过元素容器以平滑地减慢滚动的内容。

当你滚动一个元素的内容时,会发生这种情况,这个元素的动量足以让动量滚动超过滚动内容的长度。

有了这个行为, element.scrollTop属性相应地改变元素的滚动位置,并且小于0或大于最大滚动( element.scrollHeight - element.offsetHeight )。

2.溢出滚动<body>

如果您尝试滚动任何元素的最小/最大滚动位置甚至更远(顶部向上的元素或底部向下的元素),则会发生此行为。 然后滚动似乎“冒泡”到<body>标签,整个视口滚动。

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

焦点locking和行为之间切换(1.5秒延迟)

在这种情况下最令人烦恼的是,上述两种types之间的切换不能即时切换。

input其中一个后,您无法将焦点切换到任何其他元素(可滚动元素,button,链接,…),从而滚动行为也不会改变。

例如:如果你向上滚动一个元素到顶部位置,inputoverflow scrolling type 2 ,那么用户最自然的反应就是试着向下滚动。 由于焦点被locking到滚动体而不是overflow scrolling type 1所以它停留在type 2 ,整个身体向下滚动。 然后典型的用户开始任意地开始频繁地上下滚动而不会中断type 2

焦点的切换以及滚动行为的改变只能在溢出animation结束并且元素静止(甚至比0.5s更长)的时候才会发生。

从而回到前面的例子,用户的正确反应是停止触摸屏幕大约1秒-1.5秒,然后再次向下滚动。

解决scheme:

types1:

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

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

然而,这种方法禁用了浏览器原生animation滚动,因此不适用于大多数应用程序。 然而,有一些改进(只有防止在顶部滚动或底部滚动,…)这种方法修复了大多数问题。 许多可能的实现可以在这个SOpost中find。

types2:

然而,溢出滚动的身体并不能阻止上述方法。

似乎合理的一个可能的解决scheme是防止该元素处于其最高或最低位置,如上述问题的最佳解决scheme所述 。

 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上并不可靠。

所做的工作是设置position: fixed<body>元素上,以防止身体移动。 但是请注意 ,这仍然不能完全阻止type 2的发生,这就是为什么有时你不能滚动/聚焦任何元素,因为在背景type2 ,其焦点locking仍然在发生(再次,停止触摸屏幕一会儿之后它再次按预期工作)。

虽然这还远没有达到最佳解决scheme,但它似乎是我们可以获得的最好时机。

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


编辑2:确定的解决scheme

剧本iNoBounce工程奇迹。 只需将其加载到页面,体验一个无反弹的Web应用程序。 到目前为止,我还没有发现这个解决scheme的任何问题。