Prevent overflow / rubberband scrolling on iOS

25,599

Solution 1

The solution:

Type 1:

The most basic solution to prevent overflow scrolling on the element itself is to prevent default on touch events.

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

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.

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

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 stop type 2 from happening, which is why sometimes you cannot scroll/focus any element because in the background type2 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 to position: 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.

Solution 2

I see this issue is still relevant so...

Be aware of using position: fixed on body. It may do weird scroll freeze bug - actually it will still "rubberband" but you will not see it.

see: Div scrolling freezes sometimes if I use -webkit-overflow-scrolling

Share:
25,599

Related videos on Youtube

Aides
Author by

Aides

Updated on March 18, 2020

Comments

  • Aides
    Aides about 4 years

    There are already multiple questions on the topic of overflow/rubberband scrolling on SO but

    1. none of them provides a solution working for all cases on iOS 9.3.2
    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 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.

    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.

  • Aides
    Aides about 7 years
    One problem that still persists is that iNoBounce does not work with content that should be scrollable horizontalls. There is probably no solution to that at the moment.
  • evanjmg
    evanjmg over 6 years
    iNoBounce simply prevents pulling down which in turn prevents scrolling, so it breaks everything
  • Cybernetic
    Cybernetic over 5 years
    This prevents scrolling altogether. It freezes the entire app on a single, non-scrollable screen.
  • Gavin
    Gavin about 5 years
    Read the plugin's description. It says You've built a nice full-screen mobile webapp, complete with scrollable elements using the -webkit-overflow-scrolling property. You need to use the -webkit-overflow-scrolling property for elements you want to scroll. I'm assuming if you want to remove the bounce on body, you need to use an absolutely positioned, full width/height wrapper div with overflow: scroll.
  • Mike Hamilton
    Mike Hamilton over 3 years
    This isn't really a solution to the problem. Any child elements that scroll will not scroll properly when scroll chaining takes effect.