Drop-Down Navigation: Responsive and Touch-Friendly

What if you need a multi-level navigation? In most cases, you design a drop-down menu using unordered lists. But what do you do to make it usable on small and / or cursorless screens? By usable I mean being able to use hyperlinks on parental anchors and open them with a double-tap (which is a native act on touch devices), also being able to close the drop-downs by tapping anywhere outside them to avoid flashing and other huge usability faults but having a usual bulletproof drop-down menu on desktop screens at the same time. A while ago I came up with quite a simple technique. I have been successfully implementing it into my projects as there is no room for one-sided techniques anymore.

Drop-Down Navigation: Responsive and Touch-Friendly

The Problem

...is a drop-down on touch screen because there is no cursor and you cannot hover on the navigation items! It gets even worse if you have links on parent menu items. Once you click them, the drop-down shows up for a second and then immediately disappears because the browser has just opened the link of an anchor you pressed! That sucks. As well as using hash symbol for parental link href's: as a consequence, you cannot use normal URLs, the page sometimes may scroll up after the link is clicked, the drop-down may flash, you won't be able to shut it off, etc. Sorry, that is not a problem anymore – it is solved in this post! Watch my very first video montage to get the idea:

Talking about responsiveness, there is a plenty, plenty of responsive navigation solutions out there. I admit, I have hardly gone deeper into any of these, but still my favorite one is David Bushell's off-canvas navigation. It is awesome. However, as for my personal projects I fit the usual one-button responsive navigation pattern. The same one goes here as well.

Composition

The technique consists of three main parts:

  1. Simple drop-down navigation based on HTML and CSS;
  2. Responsiveness implementation using media queries;
  3. Adoption for touch screen devices with the help of a super tiny jQuery plugin.

Now about everything in turn…

HTML and CSS drop-down navigation

Firstly, simple markup of multi-leveled unordered lists with a little bit of HTML5 flavor. My example consists of two levels. Two is not the limit, you may have infinite levels on your next super project.

<nav id="nav" role="navigation">
  <a href="#nav" title="Show navigation">Show navigation</a>
  <a href="#" title="Hide navigation">Hide navigation</a>
  <ul>
    <li><a href="/">Home</a></li>
    <li>
      <a href="/" aria-haspopup="true">Blog</a>
      <ul>
        <li><a href="/">Design</a></li>
        <li><a href="/">HTML</a></li>
        <li><a href="/">CSS</a></li>
        <li><a href="/">JavaScript</a></li>
      </ul>
    </li>
    <li>
      <a href="/" aria-haspopup="true">Work</a>
      <ul>
        <li><a href="/">Web Design</a></li>
        <li><a href="/">Typography</a></li>
        <li><a href="/">Front-End</a></li>
      </ul>
    </li>
    <li><a href="/">About</a></li>
  </ul>
</nav>

Now let's just give a sense to the HTML above with the CSS below. Presenting only the principle parts of CSS. Full code – later in the demo.

Do not be surprised by Show / Hide navigation anchors. I am going to hide these for now using #nav > a selector and flick on later in the next step.

#nav {
  /* container */
}

#nav > a {
  display: none;
}

#nav li {
  position: relative;
}

/* first level */

#nav > ul {
  height: 3.75em;
}

#nav > ul > li {
  width: 25%;
  height: 100%;
  float: left;
}

/* second level */

#nav li ul {
  display: none;
  position: absolute;
  top: 100%;
}

#nav li:hover ul {
  display: block;
}

User interface behavior on non-touch screens is based on the result of position: relative and position: absolute manipulation. On touchscreens it still fails at solving the problem, but we will make it right later in the 3rd part of the post.

Responsive drop-down navigation

Let's pour some responsiveness in. 640 pixels is my choice for a major breakpoint at which with the help of a single media query, the navigation shrinks to a button (icon). Therefore, the menu can only be called out by pressing the button.

@media only screen and (max-width: 40em) /* 640 */ {
  #nav {
    position: relative;
  }

  #nav > a {
    /* ... */
  }

  #nav:not(:target) > a:first-of-type,
  #nav:target > a:last-of-type {
    display: block;
  }

  /* first level */

  #nav > ul {
    height: auto;
    display: none;
    position: absolute;
    left: 0;
    right: 0;
  }
  
  #nav:target > ul {
    display: block;
  }
  
  #nav > ul > li {
    width: 100%;
    float: none;
  }

  /* second level */

  #nav li ul {
    position: static;
  }
}

CSS pseudo selector :target makes it easy to toggle menu as the anchor's href attribute coincides with CSS ID selector name. Therefore, as you might have understood, the first anchor of #nav > a is responsible for showing and the second for hiding the menu.

Touch-friendly drop-down navigation

The most important things go in the end: let's finally solve the problem described in the very first paragraphs of the post. Here comes the promised super lightweight jQuery plugin which I called DoubleTapToGo.js and it is only 550 ridiculous bytes in size when minified.

When you tap the parent item for the first time, DoubleTapToGo prevents the browser from opening a new URL but allows that if tapped once again in succession. If there is an intermediate tap(s) between the first one and the second the counter resets. Quite simple principle of operation, isn't it? It may be hard to believe but this solved all of the flaws I have mentioned before.

Considering the markup above, the plugin should be only applied to the items that are parents – in order to avoid double-tap requirement on drop-down-less items:

$( '#nav li:has(ul)' ).doubleTapToGo();

This is it! Complete fulfillment, full code and working example – all in the demo:

Conclusion

I have ran quite successful tests on both Mac and Windows versions of the latest (April, 2013) Chrome, Safari, Firefox, Opera releases, also IE9+, iOS 6 / 7, Android 2.2 / 4.2 / 4.4, Windows Phone 7 / 7.5, Opera Mobile.

Changelog

  • Windows Phone 7.5 is tricky when it comes to :hover. Append aria-haspopup="true" to the anchors that have <ul> siblings. (2014-01-29)
&