loading...
Cover image for The mistake developers make when coding a hamburger menu

The mistake developers make when coding a hamburger menu

tongrhj profile image Jared Tong ใƒปUpdated on ใƒป3 min read

What do The New York Times' developers get wrong about the hamburger menu, and what do Disney's and Wikipedia's get right?

As far as I know, I've found only one way to style the hamburger menu's open state that supports iOS Safari. (Presumably, you want a mobile view to work on iPhones!)

It's all about how the hamburger menu is positioned.

The Problem with Many Hamburger Menus

If your hamburger menu has no need for scroll... Congratulations! The CSS solution you're thinking of now will probably work just fine: position the sidebar absolutely out of and into the viewport when the user clicks on the menu icon.

If your menu has more items than the viewport can display at once, this is what happens when your hamburger menu is positioned absolutely:

If you don't want to watch the video, I'll try and describe it in words.

  • Scrolling within the position: absolute menu is unpleasant: it does not scroll smoothly, and when it reaches the end of scroll, it does not bounce in that satisfying, patented rubber-band way. Try the hamburger menus on New York Times, or Pitchfork

  • If you overscroll in the hamburger menu, iOS Safari will scroll the body instead. Try the sidebar on Viki

  • If you tap beyond the menu, like scrolling on the sliver of main content exposed besides the sidebar, you will lose the ability to scroll within the menu. Try the hamburger menus on Grab

Notice how sometimes iOS scrolls the menu, sometimes it scrolls the body behind the menu? Frustrating!

And FWIW, you can break the scroll on Apple.com too. An easy way to trigger the scroll on the hamburger menu is to use your phone in horizontal.

The Solution

Basically, the key thing you must remember about the Menuโ€™s final, open state is this: instead of positioning the menu absolutely, it will be the main content that is positioned once the sidebar is opened. In other words, instead of positioning the menu, position everything else!

Here is that in code, alongside explanatory comments:

<html>
<head></head>
<body>
  <div class="sidebar">Hamburger menu links go here</div>
  <div class="main-content"><button class="hamburger-menu-icon" onClick="toggleSidebar()">๐Ÿ”</button></div>
</body>
</html>
/* Arbitrary CSS variable values for explanatory purposes */
:root {
  --sidebar-width: 100px;
  --sidebar-bg-colour: blue;
}

.sidebar {
  display: none;
  position: relative;
  width: var(--sidebar-width);
}

@media (max-width: 767px) {
  html.sidebar-is-open .sidebar {
    display: block; 
     /* 
      The sidebar is just rendered in default position,
      as it appears in the document flow
     */
  }

  html.sidebar-is-open .main-content {
    position: fixed; 
    /* 
     It is the main content that is positioned. 
     This is the crux of the implementation. The rest is all sugar.

     Cons: the body will scroll to the top, losing your user's scroll position
    */

    /* prevents resizing from its original full-screen width */
    bottom: 0;
    left: 0;
    right: 0;
    top: 0;
    width: 100%; 

    overflow: hidden;
  }

  /* Optional enhancement: 
     make the overscroll on iPhone the same colour as the sidebar */
  html.sidebar-is-open body {
    background-color: var(--sidebar-bg-colour);
  }
  .sidebar {
    background-color: var(--sidebar-bg-colour);
  }
}
const documentElement = document.querySelector("html");
const contentElement = document.querySelector(".main-content");
const sidebarElement = document.querySelector(".sidebar");
const sidebarIsOpen = () => documentElement.classList.contains("sidebar-is-open");

const openSidebar = () => {
  /* How you trigger the change is up to you */
  documentElement.classList.add("sidebar-is-open");
};

const closeSidebar = () => {
  documentElement.classList.remove("sidebar-is-open");
};

const toggleSidebar = () => {
  sidebarIsOpen() ? closeSidebar() : openSidebar();
};

Conclusion

So far I've found two big players who get it right: Wikipedia and Disney USA

Try their hamburger menus on iOS and see what a great experience the scrolling is!

Hopefully you can spread the word, and fix hamburger menus from now on.

(Side note: Or maybe we can just kill them altogether as a UI solution, since it hasn't bothered enough people to be worth fixing. Is anyone even using these things? Consider tab bars instead, see spotify's example)

If you're more of a beginner, you can find an explanation of what a hamburger menu is and how to build a hamburger menu from scratch on my blog.

Discussion

pic
Editor guide
Collapse
equinusocio profile image
Mattia Astorino

I just use this to lock the body on mobile:
github.com/willmcpo/body-scroll-lock

and the css overscroll-behavior: contain; to prevent overscroll

Collapse
link2twenty profile image
Andrew Bone

It's probably worth noting overscroll is still in the draft phase even if support is surprisingly good.

Collapse
equinusocio profile image
Mattia Astorino

There are a lot of "drafts" already implemented. They do it for mainly two reasons:

  • Let developers use them and give feedbacks
  • Test and improve the spec on real use cases
Collapse
assaultoustudios profile image
Vernon Joyce

My view: in a lot of cases you could get rid of the navigation altogether.

If your page has good UX and leads you through journeys effectively then in theory your user would never need to open up a menu to find something. If they do need to then search should be sufficient.

We were toying with the idea of a conversational UI in a recent project where the user would only have access to a search bar which would filter the content on the page. Never got to test this idea but it definitely solves the burger problem!

Collapse
adrianhelvik profile image
Adrian

Apple should fix their scroll issues. iOS and scrolling is a nightmare to work with.

Collapse
nbeers22 profile image
Nate Beers

Safari in general is a nightmare, both on desktop and mobile

Collapse
adrianhelvik profile image
Adrian

So many obscure bugs.. It does have good js and css support though, so it's a double edged sword.

Safari: It just works! Until one day, the screen turns white for no apparent reason and you have to do something completely nonsensical to fix it.

Had to put in a crazy hack the other day as Safari doesn't calculate the scroll height correctly when images spend some time loading and the resulting scroll height is less than 100vh (100vh doesn't take the address bar into account). This hid some necessary navigation and rendered the app unusable in this very specific edge case.

To fix it I used a capturing load event listener and set position fixed before resetting the positioning. Nasty business.

Collapse
abraham profile image
Abraham Williams

I was expecting the answer to be "implementing a hamburger menu to start with".

There are some good alternatives available.

Collapse
bbjfdez profile image
Sergio Fdez

Working fine on Android :)

Collapse
pklapperich profile image
pklapperich

NYT and Gab both have issues in Chrome on Android.

For NYT, the page content scrolls when the hamburger menu over scrolls.
For Gab I was sometimes able to scroll in the sliver of content on the right. After that, menu scrolling was broken and I was even able to scroll horizontally with the menu open, which was weird.

Collapse
ashleyjsheridan profile image
Ashley Sheridan

Given that iOS is the lesser-used mobile operating system, I'd say it's up to them to fix their bugs, not up to all the web developers to work around Apples behavior.

Collapse
bjornbreck profile image
Bjorn Breckenridge

It's also worth noting that you're on an iPhone and it's common sense to add a class to see the body to fixed whilst the menu is open.

Collapse
davidloop profile image
David Loop

iOS momentum scrolling.
-webkit-overflow-scrolling: touch;