Multi-level menu in Express
I created the single level menu at first and it was working fine. I also noticed that on almost all pages I’m working with, the menu has more than one level. I decided to handle this at the beginning of the development process to make sure that I can achieve my goal in handlebars.
Menu preparation and highlighting
My old function to generate menu will not be good enough to handle highlighting. I prepared the new one to do only this (to highlight proper menu item) and separated it from menu generation. Now in my modules/tools.js, I have:
exports.generateMainMenu = function(req, res, next) { var menuItems = [{ label: 'Home', href: '/' }, { label: 'About Us', href: '/about', menuItems: [{ label: 'About our staff', href: '/aboutstaff', menuItems: [{ label: 'CEO', href: '/aboutCEO' }, { label: 'CTO', href: '/aboutCTO' }] }, { label: 'About our building', href: '/aboutbuilding' }] }, { label: 'Admin', href: '/siteAdmin', menuItems: [{ label: 'Setup', href: '/siteAdmin/setup' }] }, { label: 'Users', href: '/user' } ] adjustMenuClass(menuItems, req._parsedUrl.pathname); res.locals.menuItems = menuItems; next(); };
As you can see, I placed menuItems structure as the sub-menu of given menu. This allowed me to create such adjustMenuClass function as follows:
function adjustMenuClass(menuItems, pathname) { for (var item in menuItems) { if (menuItems[item].href == pathname) { menuItems[item].class = 'menu-selected'; } if (menuItems[item].menuItems) { adjustMenuClass(menuItems[item].menuItems, pathname) } } }
This function is working recursively and is handling highlighting on all levels of my menu. Variable pathname which is simply the URL part is taken from req._parsedUrl.pathname.
Displaying on the site
To display the menu on the site properly, I had to adjust my previous code. I had to split into more pieces (partials) and load them properly. I was not sure if partials are able to do the recursive loading. I was happy when I found that this is possible and rather easy. Right now in my layout.hbs I have:
<!-- ... --> <div class="header"> {{> mainMenu}} </div> <!-- ... -->
This is loading mainMenu.hbs partial which looks like this:
<nav> <label for="toggle-mobile-menu">&#9776; Menu</label> <input id="toggle-mobile-menu" type="checkbox" /> {{> menuList}} </nav>
And again, the partial is loaded – it is menuList.hbs which is implemented this way:
<ul> {{#each menuItems}} <li class="{{ class }}"> <a href="{{ href }}">{{ label }}</a> {{#if menuItems}} {{> menuList}} {{/if}} </li> {{/each}} </ul>
And here finally you can see that this particular partial is loading itself if menuItems of lower level exists.
Simple responsive menu
You probably noticed that there are label and checkbox created for the menu with the mobile classes applied – let’s take a look at this for a moment. I was looking at the simple and easy to implement multi-level responsive menu. I found pure CSS menu on the blog of Aamir Afridi. Of course, I adjusted it to meet my goals, but most of the code remained unchanged so I will not copy and paste it here. As usual, you can find this project source on GitHub.