There are already multiple questions on the topic of overflow/rubberband scrolling on SO but
- none of them provides a solution working for all cases on iOS 9.3.2
- none of them gives extensive and complete information on the problem itself
which is why I created this post as a body of knowledge.
The problem:
The thing that was never mentioned in any other post is that iOS overflow scrolling is actually a two part behaviour.
1. Overflow scrolling of content with overflow: auto/scroll
This is the commonly known and mostly wanted behaviour of a element with -webkit-overflow-scrolling: touch
where the continuous/momentum scrolling behaviour goes past the elements container to slow the scrolled content down smoothly.
It happens when you scroll the content of an element with a momentum high enough for the momentum scrolling to go past the length of the scrolled content.
With this behaviour the element.scrollTop
property changing accordingly to the elements scroll position and being less than 0 or bigger than the maximum scroll (element.scrollHeight - element.offsetHeight
).
2. Overflow scrolling of <body>
This behaviour occurs if you try to scroll any element already at its minimum/maximum scroll position even further than that (an element at top upwards or element at bottom downwards). Then the scroll seems to "bubble up" up to the <body>
tag and the whole viewport is scrolled.
In contrary to above here the element.scrollTop
property does not change but document.body.scrollTop
changes instead.
Focus lock and switching between behaviours (1.5s delay)
The most irritating thing in this context is that the switch between the two types described above does not switch instantly.
After you enter one of both you cannot switch focus onto any other element (scrollable elements, buttons, links, ...) and thereby the scrolling behaviour does not change as well.
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 tooverflow scrolling type 1
it stays intype 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 oftype 2
.
The switch of focus and thus the change of scrolling behaviour can only occur after the overflow animation has finished and the element stands still (even a bit longer [around 0.5s] than that).
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.
The solution:
Type 1:
The most basic solution to prevent overflow scrolling on the element itself is to prevent default on touch events.
This method however disables the browsers native momentum scroll and is thereby not suitable for most applications. With some refinement however (only prevent if at top scrolling up or at bottom scrolling down, ...) this method fixes most problems. Many possible implementations can be found in this SO post.
Type 2:
Overflow scrolling on the body however is not prevented by methods described above.
One possible solution which seems reasonable is to prevent the element from ever being at its top or bottom position as described as best solution on mentioned question.
This however did not reliably work on iOS 9.3.2.
What did work however is setting
position: fixed
on the<body>
element to prevent the body from moving. Please note however that this still does not completely stoptype 2
from happening, which is why sometimes you cannot scroll/focus any element because in the backgroundtype2
with its focus lock is still happening (again, after you stop touching the screen for a moment it again works as expected).While this is still far from being the optimal solution it seems to be the best we can get for the time speaking.
Edit: Please note that I am not sure if it is safe to put
position: fixed
on a<body>
element. To track possible issues I have created following SO post. Apparently it might be better to create a wrapper element as child of body and set that element toposition: fixed
to avoid zoom problemes.Edit 2: The definite solution
The script iNoBounce works wonders. Just load it to the page and experience a bounce-free web application. So far I have not found any problems with this solution.