How to make a scrollable menu without scrolling the body

A properly working solution to the problem of touch devices and their disregard for overflow: hidden; when scrolling.


TLDR;

If you're in a hurry, you can check out the github repo which also contains a live demo. Hint: check out this file

*Before we begin, this article requires you to have a basic understanding of responsive development with Javascript and CSS however you can clone the repository and customize it to your projects without having to fully understand how this works.

Okay, having that out of the way let's dig into:

The problem

Making a menu behave well on desktop and mobile is an art in itself, there are many things to get right for the best user experience (making the hit-boxes on links and buttons large enough, having smooth animations, intuitive user interface, the list can go on) however on thing that I rarely see addressed properly is preventing the body from scrolling when a user opens a menu on a mobile device. This is especially annoying when the menu is too long to fit the screen so you end up having a weird blend of also scrolling the menu and the body.

Some incomplete fixes

A Google search for this problem will tell you to add overflow: hidden; to the body which works on desktop but touch devices ignore this CSS rule when it comes to scrolling so you might be tempted to think...

Oh, I can just disable touchstart, touchmove with Javascript when the menu is opened and it will be fixed: (sips coffee)

$('.hamburger').click(function() {
    var self = $(this);

    self.toggleClass('open');
    $('.nav').toggleClass('open');

    if (self.hasClass('open')) {
        $('html, body')
            // Needed to remove previously bound handlers
            .off('touchstart touchmove') 
            .on('touchstart touchmove', function (e) {
                e.preventDefault();
            });
    } else {
        $('html, body')
            .off('touchstart touchmove') 
            .on('touchstart touchmove', function (e) {});
    }
});

You're happy to make it work properly, go home and feel satisfied that you once again, can tell your wife how saved the day with your creative problem solving.

The next you come in and your boss tells you that the menu needs an extra 7 items, each expandable with sub-pages AND contact info and links at the bottom... You stare at your e.preventDefault() and with a broken heart you mercilessly delete that event handler.

Back to the drawing board: you read tons of blog posts and Stack Overflow threads but all of them revolve around the same ideas.

The solution

Open this page on a mobile device and test it out:
Rock Your Body, Scroll Your Menu | Demo

The source code:
malinushj/rock-your-body-scroll-your-menu

How it works

I will not include in the explanation any of the CSS and HTML that has to do with design and layout, just what's needed for this to work.
You can have a look through the source code but...
Down to its core, it all boils down to the following ingredients:

HTML
<!-- This will be element to which we add a class "open" whenever the hamburger button will be clicked -->
<nav class="nav">
    <div class="nav__container">
        <!-- Your content: menu, info etc. -->
    </div>
</nav>
CSS
.nav {
    /* Basic stuff for making the nav fullscreen */
    display: block;
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 9999;
}

.nav__container {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    /* The main point: */
    overflow-y: auto;
    /* Optional but highly reccomended: enables momentum scrolling on iOS */
    -webkit-overflow-scrolling: touch;
}

html {
    /* For desktop */
    overflow: hidden;
}
body {
    overflow: visible;
    height: 100%;
}

In the real world you would have Javascript mixed in as well but the main takeaway is in the CSS, having all your menu content inside a .nav__container with position: absolute; and overflow-y: auto;.

...And that's it

The solution isn't too complex but it sure took me many hours to find it.

Thank you for reading!


Have any questions, ideas or need help?

Drop me a line at malin.branduse@gmail.com