Tuesday, November 2, 2021

Build a Multilevel Animated Mobile Menu With JavaScript

Build a Multilevel Animated Mobile Menu With JavaScript

One of my first tutorials here in Tuts+ back in 2015 covered the creation of an off-canvas sliding menu with jQuery.mmenu. If you check the demo project, you’ll notice that the menu includes multiple levels.

Today, we’ll discuss how to create a three-level deep sliding mobile menu without using any library, just with pure JavaScript. By default, the first one will appear as soon as we open the menu. The other two will come into view with a slide animation upon request.

Ready for another challenge?

Our Multilevel Menu

Check the menu implementation on the pen below:

1. Define the HTML Markup

The markup for our mobile menu will consist of the following elements:

  • A header with a nav inside it. 
  • A main where the main content of our page will live.

Inside the nav, we’ll put two divs. The first one will have the header-bar class, while the second will have the menu-wrapper one.

The .header-bar will consist of three elements:

  • The toggle menu
  • The company logo
  • The company's Twitter account

Inside the .menu-wrapper, we’ll put three divs with the class of list-wrapper. We’ll call them panels for simplicity. Things to note:

  • In the first panel, we’ll specify the menu structure with parent and child menu items. To do this, we’ll use a typical markup with nested unordered lists.
  • The second and third panels will contain a back button and an empty div with the class of sub-menu-wrapper. More about their job:
    • The back button will help us go up one level. That said, from level three to level two and from level two to level one.
    • The .sub-menu-wrapper of the second panel will contain the second-level links. In the same way, the .sub-menu-wrapper of the third panel will hold the third-level links. We’ll insert these links dynamically through JavaScript.

With all the above in mind, the following markup comes up:

2. Specify the Main Styles

Let’s now concentrate on the header styles for our mobile menu.

For the sake of simplicity, I won’t walk through all the styles, but feel free to look at them by clicking on the CSS tab of the demo project.

Some things to note:

  • The header will be a fixed positioned element and have a maximum width of 600px.
  • The .header-bar will have a fixed height of 60px.
  • The .menu-wrapper will be absolutely positioned and sit underneath the .header-bar. Furthermore, it will have a height equal to the viewport height minus the .header-bar’s height. Lastly, it will initially be hidden.
The header layoutThe header layoutThe header layout

The associated styles:

Let’s continue with the panel styles.

  • All of them will receive their parent's height and have overflow-y: auto. This property ensures that a scrollbar will appear in case there are a lot of menu links inside it.
  • Especially the second and third panels will be absolutely positioned and out of the screen by default.

The associated styles:

Next, we'll hide all nested menus from the first and third panels:

Moving on, we’ll set some styles for the mobile menu links, specifically:

  • Links that open a nested menu will be underlined.
  • To indicate that a link is active or hovered, we'll give it a different color and a character.
The link appearance when is active or hovered

The associated styles:

Finally, we’ll specify some straightforward styles for the back button.

The back button

Here they are:

3. Add the JavaScript

After setting up the styles, it's time to discuss the actions needed for revealing the nested menu levels with slide animation. 

Toggle Menu

Here’s an animated GIF that illustrates the menu’s toggle state:

The menu statesThe menu statesThe menu states

Each time we click on the toggle button, we’ll perform the following actions:

  1. Toggle the menu’s visibility via the is-visible class. If it’s hidden, it will appear and vice versa.
  2. Check if the menu is closed. If this condition is fulfilled, we’ll do the following:
    1. Remove the is-visible class from the second and third panels if they have it.
    2. Remove the is-active class from the active menu links if there are such elements.

Here’s the required JavaScript code:

And the relevant styles:

Open Level Two

Here’s an animated GIF that illustrates how the second level menus will appear:

The second level of menuThe second level of menuThe second level of menu

Each time we click on a menu link of the first level (the visible ones), we’ll check to see if it has a nested menu as a sibling. If this condition is fulfilled, we’ll perform the following actions:

  1. Prevent its default action.
  2. Assign the is-active class to it.
  3. Create a deep copy of its sibling.
  4. Append this newly created node into the .sub-menu-wrapper of the second panel.
  5. Reveal the second panel with slide animation.

Here’s the required JavaScript code:

And the associated styles for the animation:

Open Level Three

Here’s an animated GIF that illustrates how the third level menus will appear:

The third level of menuThe third level of menuThe third level of menu

Remember that by default, there isn’t any content inside the .sub-menu-wrapper of the second panel. In the previous section, we described how it receives content by performing a deep copy. 

Here’s an example of the generated markup:

The contents of the second level when becomes visibleThe contents of the second level when becomes visibleThe contents of the second level when becomes visible

Next, we have to perform some actions as soon as someone clicks on a menu link of the second level. However, the tricky thing is that these links are dynamically generated and aren’t part of the initial DOM. That said, the click event won’t work for these elements :(

Happily, the solution is simple enough. Thanks to the event delegation, we’ll attach the click event to the parent panel which is part of the DOM. Then, via the target property of that event, we’ll check the elements on which the event occurred to ensure that these are links with a sub-menu sibling.

Assuming these are the target elements, for each one of them, we’ll do the following things (similar to the previous section):

  1. Prevent its default action.
  2. Assign the is-active class to it.
  3. Create a (deep) copy of its sibling. Note that there won’t be any change if we do a deep copy or not, as our menu contains three levels. 
  4. Append this newly created node into the .sub-menu-wrapper of the third panel.
  5. Reveal the third panel with slide animation.

Here’s the required JavaScript code:

And the relevant styles for the animation:

Go Back One Level

Here’s an animated GIF that illustrates how the back buttons will work:

Go back one levelGo back one levelGo back one level

Each time we click on a back button, we’ll perform the following actions.

  1. Find the parent panel of the target back button and remove its is-visible class.
  2. Remove the is-active class from the active link of the previous sibling of the parent panel. 

Here’s the required JavaScript code:

And again, there are some accompanying CSS styles for this action.

Conclusion

In this tutorial, we created a multi-level mobile menu by taking advantage of some common CSS styles and JavaScript DOM’s API. Most importantly, our initial markup contains simple nested lists for menu creation. That means we can transpile it into a dynamic one and take advantage of a CMS’s features like WordPress with just a few modifications.

I’m sure there will be other solutions for building such menus, perhaps more effective or accessible than this one. Our approach takes advantage heavily of the cloneNode() method that creates duplicate nodes, so it should be used with caution, especially if our sub-menus contain IDs.

Here’s a reminder of our today’s exercise:

If you liked this exercise and it saved you some time for building such a solution from scratch, don’t forget to give it some ❤️. Plus, if you’d like to see another version or an extension of this menu, let us know!

Last but not least, if you find the idea of building custom WordPress menus challenging, I’ve good news for you: some of my upcoming tutorials will target this topic. Stay tuned!

As always, thanks a lot for reading!


No comments:

Post a Comment