New theme in development and is currently live on - Please check back often for updates (11/12/16).
jQuery Logo

Front-End · Tutorials

Responsive Multi-Level Menu with jQuery


In this tutorial I will show you how to build a responsive multi-level menu which is touch driven and powered by jQuery.

There is now a jQuery plugin coupled with this postDownload CeNav

Whilst working on Surface I wanted to build a touch driven menu system for the primary navigation area, this was also a design requirement for Centro which aims to implement the Theme Unit Test as described in the codex.

1) Background and Requirements

One of the criterias is that a theme supports a menu (WordPress nav menu) to three levels deep.

So, whilst Surface has an ok menu system it’s not without bugs and could be scrutinised more. The aim with Centro was to eradicate some of static behaviours which Surface was affected by and adhere to the Theme Unit Data requirements.

2) Markup Requirements

The markup for a solid menu system would simply be a set of unordered lists with list items, anchors and further unordered lists. The list items with further nested items would hold an anchor tag within the element and a span tag within that, which when clicked will show the adjacent menu. Accessibility wise, it may be more suited to use a button instead of a span.

3) The jQuery

To get started when a span is clicked we want to cancel the action it would take when nested inside of an anchor which would be by default to follow a hyperlink.

Let’s get started…

$('ul li a span').on('click', function(e) {

Simply put we are delegating a click handler to the selector ul li a span, passing the event captured by the e variable and then running the jQuery function preventDefault();. We could also return false;.

4) CSS

In order to make the menus visible we will add a CSS class to the unordered lists which should be shown, we’ll also add another utility class to the actual span tag. This way it’s easier to change the visuals for better user experience.

$('span').on('click', function(e) {
    $(this).toggleClass('close').closest('li').find('> ul').toggleClass('active');

The CSS added would simply be:

.active { display: block; }

A style for the close icon would also be appropriate as mentioned, try setting up the code explained so far and familiarising yourself with the concepts.

You may want to be more specific with the selector in the .on() method, like the following:

$('nav ul.primary li a span, nav ul.secondary li a span').on....

This way the menu system will only be applied to primary and secondary unordered lists nested within a nav element.

5) Multi-Level

There is a situation the menu begins to break and that’s when we are in between menus, let’s say between tier 1 and tier 3 in which tier 1 and tier 2 are already shown but you’re about to click a span within the tier 2 menu to expose tier 3. Our classes will be toggled and menus closed not opened. We need to distinguish between menus that are 1 or more levels deep compared to a simple open / close situation.

$('ul li a span').on('click', function(e){
    // Tier 1+, 2+, 3+, 4+, 5+
    if($(this).parents('ul').length > 1) {
        $(this).toggleClass('close').closest('li').find('> ul').toggleClass('active');
    // tier 0 to 1 only
    else {
        if(!$(this).hasClass('close')) {
            $('ul li a span, ul.sub-menu').removeClass('close active');
        $(this).toggleClass('close').closest('li').find('> ul').toggleClass('active');

6) Summary

That’s it, the entire menu system, code to be ran for 1 to 1 menus and code to run for anything more. The code could be optimised by caching variables to eradicate any ‘dom diving’ that’s happening.

Thanks for reading, leave a comment if you have any problems or suggestions.


The previous touch menu in Surface wasn’t without its problems, again the aim was to fix and expand for Centro.

This was the initial refactoring, but it wasn’t quite there.

See the Pen jVvqmQ by buddycore (@buddycore) on CodePen.

A friend offered to help with the logic.

He came up with a working solution as shown here.

See the Pen BuddyCore Submenu Solution by Michael Goldspinner (@emgo) on CodePen.

Leave a Reply

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

Loading more content...
Minimum Requirements

Your device does not meet the minimum requirements to view this site, please considering increasing your device width to more than 300px.