Creating a Basic Mobile Menu Using JavaScript

Published Aug 16, 2023
Updated Aug 17, 2023
By Simon

In the last year, I have had to create and optimize many a mobile navigation and most of the time they have been hamburger menus. Even though the `ole hamburger gets a hard time it has become a staple of almost every website. And one of those UI elements that every front-end developer needs in their toolbox.

In this article, we will create a basic mobile menu. Then we will look at some features that every menu should have to make them usable, accessible, and fun to use.

What we will learn

  • Semantic HTML markup
  • Flex 
  • Positioning
  • Event Listeners
  • Adding, removing and toggling classes

First up, let's set up our page. I'm going to use a template from GitHub that has content in it so that I have vertical scroll on the page. The template also has separate CSS and JavaScript files. If you want to use that you can grab it from GitHub. Make sure you are signed in, visit the link below, and click the Use this template button to create a new repository.

Full Page Template:  https://github.com/siramsay/full-page-template

With a full HTML page with some dummy content blocks set up, we now need to add the markup for our mobile navigation.

Setting up the markup for a mobile navigation

Add the menu and button markup for the hamburger menu

  • First, at the top in the header, we will add the nav element with a menu/ul inside. It is a good idea to have this first on the page so that it is first in the tab order. The only navigational element that might come before the menu is a skip to the main content link.
  • Next, add an element with a word menu in it and give it a class I add this before the menu nav element. This should be a <a>, or a button. I am using a button.

Below is what your code should look like.

<header id="header">
  <h3>The Mob</h3>
  <button class="mobile-menu-toggle">
    Menu
  </button>
  <nav class="mobile-menu">
    <menu>
      <li><a href="#p">Product</a></li>
      <li><a href="#a">About</a></li>
      <li><a href="#c">Contact</a></li>
      <li><a href="#h">Home</a></li>
    </menu>
  </nav>
</header>

Adding CSS to style, position and hide the menu

It is up to you how you style this but I am going to keep it simple for now. In the CSS file add the rules outlined in the following steps.

  • Add CSS to style to the menu li items and style all link pseudo-classes: :link, :visited, :hover, :active, (LVHA order)
menu li {
  list-style: none;
  padding: .5rem;
  margin-bottom: 1px;
}
a:link {
  color: black;
  background-color: pink;
  padding: .5rem;
  text-decoration: none;
}
a:visited {
  background-color: lemonchiffon;
  color: orangered;
}
a:hover {
  color: #ffffff;
  background-color: salmon;
}
a:active {
  background-color: #0fadff;
}
  • Position the menu and button.  
    In my example, I use flex to position the header elements . If you use the template below is the diff for the header.
header {
  height: 200px;
  position: fixed;
  width: 100%;
  z-index: 100;
  background-color: hsl(60, 89%, 52%);
  font-size: 1.2em;
  color: hsl(197, 58%, 8%);
  display: flex;
  align-items: center;
-  justify-content: center;
+  justify-content: space-between;
+  padding: 2rem;
}
  • Position the nav absolute and hide it.
nav {
  display: none;
  position: absolute;
  background: rgba(255,255,255,0.5);
  width: 100vw;
  height: 100vh;
  top: 0;
  left: 0;
}

Add the CSS for the menu for when it is open by adding the below rule.

  • Add the rules for flex in the main nav rule.
  • Centre the text.
nav {
  display: none;
  position: absolute;
  background: rgba(255,255,255,0.5);
  width: 100vw;
  height: 100vh;
  top: 0;
  left: 0;
+  /* Style for flex */
+  align-items: center;
+  justify-content: center;
+  text-align: center;
}
  • Add a new rule with the class open and add display flex.
nav.open {
  display: flex;
}
menu {
  padding-left: 0;
}
  • Finally as shown above, I added padding-left zero since the menu items browser agent CSS has a left padding and I don't have a reset. (Note to self: Need to add reset to the template.)

If you open this up in the browser dev tools and then manually add the open class to the nav element the menu will show.

This is the least amount of CSS you'll need. We will look at adding more CSS to add a nice open transition in the make it shine section. But first, let's get the basic open and close functionality set up. We could use CSS for this but let's use JavaScript for now.

Add Basic JS to show menu and close menu

So now on to the JavaScript. In the actions.js add the below script.

const menuToggle= document.querySelector('button.mobile-menu-toggle');
const mobileMenu= document.querySelector('nav.mobile-menu');
menuToggle.addEventListener('click', function () {
  mobileMenu.classList.add('open');
});

The above script is all we need for a simple menu. Below is the line-by-line explanation.

  • First, we need a reference to the elements we want to interact with, namely the menu open and close button and the nav element that wraps the menu. We do this by creating constants for the elements and using the query selector instance method to select the element to assign to the constants.
  • Then we add a click event handler to handle what happens when someone clicks on the menu button.
  • We pass in a function as the second argument. The function is very simple, it adds an open class to the nav element.

This is it, someone can now open the menu and use it. However, we really should add a close button so let's do that next. 

Using .toggle() instance method on the classList read-only property

Before we work on the close button I will note you could use the .toggle instance method.

mobileMenu.classList.toggle('open');

This does as it says, it will act as a toggle. So if an open class is present it will remove it else it will add it.

One thing you will notice with the current setup is that the menu button is under the mobile menu. Note that the element is still accessible in that you can use it with the keyboard, it is still in the tab index, it just isn't clickable with a mouse. Add the following code to fix this.

.mobile-menu-toggle {
 cursor: pointer;
 z-index: 10;
}

Great now the menu button is on top and accessible to the mouse. I also added the cursor property.

Creating a toggle conditional

As great as the toggle is, you may want to use a conditional so that you can modify other attributes of the menu button or the inner HTML when clicking to open the menu. Let's see what I mean by this.

When you open the menu one of the things you will want to do is to change the word menu to close.

Note: The functionality could be done in CSS and I will look at a CSS solution later when we add a hamburger icon to the menu button.

To use a conditional in place of the toggle instance method, we can use the contains method to check for the open class. This is done in our conditional expression in the parenthesis of our if statement. In the then part of the conditional statement in the curly braces, we then remove the class open. In the else block, we add the open class.

menuToggle.addEventListener('click', function () {
  if(mobileMenu.classList.contains('open')){
    mobileMenu.classList.remove('open');
    menuToggle.innerHTML = 'Menu';
  } else {
    mobileMenu.classList.add('open');
    menuToggle.innerHTML = 'Close';
  }
})

In the above code, you can also see that I have modified the innerHTML so when the menu is open it reads Close else it reads Menu.

To see the working solution please check the demo at https://designkojo.com/demos/mobile-template/

Nice work, we now have the basics of a mobile menu 

I hope you have learnt something. If so, please sign up for my newsletter below. I write about front-end development and design; HTML, CSS and Javascript and also frames such as Vue.js & Drupal for front-end and how to leverage Drupal as a CMF.

In the following article, Making a Mobile menu shine: accessibility Considerations and Aesthetic Tweaks, we are going to look at some accessibility and aesthetic improvements. Let's make our mobile menu shine. 

Until then seize the day!