防止在iOS上溢出/橡皮筋滚动
SO上的溢出/橡皮圈滚动的话题已经有多个问题了
- 他们都没有提供适用于iOS 9.3.2的所有情况的解决scheme
- 他们都没有提供关于问题本身的广泛和完整的信息
这就是为什么我把这篇文章作为一个知识体。
问题:
在其他文章中从未提及过的事情是,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,链接,…),从而滚动行为也不会改变。
例如:如果你向上滚动一个元素到顶部位置,input
overflow 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的任何问题。