We're constantly developing and styling drop-down and mobile menus - to speed up development we've developed a Sass/Compass driven solution, for easy configuration and re-use.

See the Pen Kozvn by Ryan Hewitt (@mesuva) on CodePen

Resize your browser or rotate your device to see it change between drop downs and a mobile nav.

One of the most challenging tasks a web developer faces is the development and styling of a drop down menu, or at least, that's been our experience. Now that it is common practice for websites to be browsed on mobile and tablet devices, the challenge is compounded as menus need to be responsive and work with touch events.

A highly successfull script for creating drop down menus has been Superfish, something we've used countless times on websites. When we started developing mobile responsive sites, we also discovered an excellent mobile menu by Astuteo - we integrated both together to develop a re-usable solution that could be dropped into site builds. By combining these two components, we were able to create a mobile responsive menu that could both handle drop downs and a mobile specific menu with menu button.

These scripts are amazing pieces of code, but in our development process we encounted some recurring issues and challenges (these aren't critisims of these scripts, simply what wasn't working for us):

  • The default styling of Superfish features borders and other styling that would would take a bit of work to remove or override.
  • The default styling of astuteo's mobile menu (as lovely as it is), is also quite hard to override and unpick.
  • We now manage our styling tasks with Sass and Compass, so the vanilla CSS of the scripts didn't really fit our new ways of working.
  • Neither script was really designed to work with each other - I had to write some quite curly jQuery to handle the transition between mobile and desktop menus, adjusting/reinitialising the scripts every time the viewport changed size.
  • Sometimes we wanted to style sub menus quite differently from higher up ones, quite a fiddly task.
  • We started to move away from the idea that on a mobile menu you would tap once to open a sub menu and once again to actually visit a top level page, we wanted something more intuative on our sites, more along the lines of http://jasonweaver.name/lab/flexiblenavigation/ or http://adnantopal.github.io/slimmenu/.

Ulimately we realised we needed a dropdown and mobile menu solution that:

  • We could drop into a Sass project as a 'partial' (a separate file).
  • We could configure using Sass variables (colours, font sizes, spacing or booleans to turn on and off aspects).
  • Was effectively style-free, providing the basic structure and nothing else.
  • Presented a mobile menu with tap areas to expand sub-menu options.
  • Could handle three levels of menu on a traditional desktop drop down menu.
  • Had very minimal jQuery/Javascript, that handles both mouse clicks and touch interactions, handling the differences between iOS and Android.
  • Had Javascript that had zero browser specific hacks or detections (Modenizer accepted), just normal jQuery calls.
  • Required no re-initilisation when resizing through breakpoint sizes.
  • Had to work with standard un-ordered lists for menus (specifically something we could easily achieve with a concrete5 autonav).
  • Supported IE8+ and all other commonly used browsers.
  • We wanted both the drop down and mobile menus to work correctly with touch events and click events - we didn't want to force touch device users to use a mobile nav if they had plenty of screen real-estate.

So we instead investigated very light-weight drop downs and developed our own Sass based, mobile responsive drop down menu. In no way can I claim that it is perfect, but we think it's worth sharing an an example of how to tackle such a problem using Sass and jQuery. We're already using it in production and it's already saving us time and our sanity.

The HTML

Our menu is a basic un-ordered list, wrapped by a div, which also includes some empty markup to create a mobile menu button. 

The only addition to un-ordered list markup is the inclusion of the class 'nav-dropdown' to list items that have a sub menu - this simply makes things a little easier to target. With our preferred CMS, concrete5, this is easily achieved with a quick override of the autonav block and the uncommenting of a single line - the code is already in the default block template.

<div class="sitenavigation">
	<span class="menu-icon"> 
		<span class="line"></span> 
		<span class="line"></span>
		<span class="line"></span>	 
	</span>

	<ul>
		<li><a href="#">First Item</a></li>
		<li class="nav-dropdown"><a href="#">Second Item</a>
			<ul>
				<li><a href="#">First Sub-nav item</a></li>
				<li><a href="#">Second Sub-nav item</a>
			</ul>
		</li>
		<li><a href="#">Third Item</a></li>
	</ul>
</div>

The jQuery

The jQuery for this script just needs to be included in a .ready function. I've put a few comments in the script to explain what each section does, but as an overview there is:

  • Something to show/hide the mobile menu in the click of the menu button
  • A function to uniformly handle touch events across different platforms. iOS is actually the culprit in this, in that it handles hover events whereas other platforms don't. This function uses the 'touchend' event and a dynamically added class to work out if a parent item has been clicked on one (i.e to unhide a submenu), or two times (to follow the parent link)
  • A function to handle the expansion of sub menus on mobile menu. This is handled by detecting a click on the LI of the parent item, unhiding the child UL 
  • A function to stop the propergation of certain click/tap events that cause problems
  • A hover function to nicely show and hide the drop down menus on desktops, with a basic jQuery fade.

The idea with this code is that it's minimal and still able to be customised.
Note the use in a few places of if (!$('.menu-icon').is(':visible')) - this is a way to detect what 'mode' we are in when events happen, with the menu button only visible in mobile modes due to the CSS and breakpoints. This was a much preferred way of detecting this, rather than trying to use screen or breakpoint detection.

Apart from being dependent on jQuery 1.7+, the only dependency is the inclusion of a Modenizr script, configured for Touch Events, with Add CSS Classes selected. This takes seconds to generate via Modenizr's download page, just uncheck anything pre-selected, select Touch Events and 'Add CSS Classes' and generate the snippet.

The Sass (SCSS)

The aim with the Sass for the menu is that commonly adjusted values such as colours and font sizes can be adjusted using variables at the top of the script, some of which are used to calculate spacings for submenus automatically. I won't go through it in detail, as the comments and nesting itself does explain most things.

It does invite direct editing, as we've tried to strike a balance between having useful variables but not overdoing it, it's still something to be customised with each project. For example, you might want change the subnav indicators to a different icon or style the third level sub-navs differently - the Sass nesting should make it a little easier to find where these things need adjusting.

Want to play?

See the CodePen entry at the top of this post for an HTML example, the Sass and jQuery code.

I'll continue to adjust this code to solve problems and make it more reusable.

-Ryan