How to avoid scroll leakage when scrolling a fixed menu on touch devices

When I was working as a frontend developer for a retailer, developing a web shop, I stumbled upon a tricky problem.
My customer was pretty picky, and didn’t want any edge case flaws.

We had a fixed (or sticky of you supported that) navigation that was always attached from top to bottom.
The nav did only cover about 2/3 of the screen’s width though, so when the user reached the end of the content,
you could see how the background started scrolling instead. The scrolling was leaking upwards to the body.

Note: This has been fixed in iOS 8 but is still a valid defect in iOS 7 and below, as well as Android.

To fix this you need to listen to the touchstart and touchmove events and check where you are in the content and which direction you are scrolling.
When you have reached the bottom of the container and the direction you are scrolling is downwards we simple use preventDefault() to stop the scroll event,
to avoid it leaking to the body content.

The wrapper covers the whole screen so when user scrolls outside of the menu, it will still scroll the menu.
It’s using a semi-transparent color to appear modular.

The content covers the width of the menu.

I also need to mention that this solution is not perfect on Android devices.
If you scroll almost all the way to the bottom, and then scrolling past the bottom without lifting you finger, the scroll will leak with the following error message in the console:

Ignored attempt to cancel a touchmove event with cancelable=false, for example because scrolling is in progress and cannot be interrupted.

A way around this, is to implement a custom scroll like iScroll, this is what we ended up doing, although not optimal, of course.

Here’s the JavaScript fixing the problem, I have explained the code with comments inside the script itself.

Try it out on your device:
https://jonwallsten.com/codepen-examples/example1/

See the Pen RNMrmP by Jon WAllsten (@JonWallsten) on CodePen.

2 thoughts to “How to avoid scroll leakage when scrolling a fixed menu on touch devices”

  1. Very nice work. A few issues I have noticed:

    1. On a Samsung S5 in Firefox mobile, I am able to induce scrolling of your background using long presses followed by a drag/swipe. I presume this is because the touchmove is not seeing this as a touchmove event, and therefore, not applying preventDefault to halt scrolling of the background. Is there any easy way to deal with this?

    2. On a Samsung S5 in Chrome mobile, I see what you mean about scrolling near the bottom (I don’t see this in Firefox mobile BTW). I see that the main content continues scrolling and also that the menu does not stay at the bottom but instead rises up (the height of the address bar) and then snaps back down when the touch is released. After applying your code to my site, I don’t have the first problem (CSS differences?), but I do have the menu rise/snap back issue. One thing that I noticed on both your menu and mine is that the address bar hides/reveals before any scrolling on the main content, but hides/reveals after the scrolling (e.g., when the menu has reached the top or bottom) on the menu. I’m thinking that an easy fix to the rise/snap back issue would be to cause the browser to apply the same scroll behavior for the menu as the main content; this should fix it since the address bar will be fully hidden or revealed before the end of the scroll. Any idea how to do this or otherwise address this problem?

    Thanks,
    Bryan

  2. Hi there

    I too have been trying to resolve the ‘scroll leak’ (liking this term as it fits very well). I tested your use case on my Motorola Moto G 4G (2nd gen) on Lollipop and the fix disables scrolling of the main content :s

    If I find any resolution to this I will let you know.

    Dave

Leave a Reply

Your email address will not be published. Required fields are marked *