Submenu list menu with three levels

Hi!
I have a menu that is made up from nested lists on three levels. The elements in the second level are open only when an element is clicked (This is what I want), but the elements on the third level is already opened.

This is the html code:

<nav id="menu">
<header class="major">
<h2>Menu</h2>
</header>
<ul>
    <li><a href="index.html">Homepage</a></li>
    <li><a href="introduzione.html">Introduzione</a></li>
    <li>
        <span class="opener">Arcani maggiori</span>
        <ul>
        <li><a href="arcani-maggiori-il-matto.html">Il Matto</a></li>
        <li><a href="arcani-maggiori-il-mago.html">Il Mago</a></li>
        <li><a href="arcani-maggiori-la-papessa.html">La Papessa</a></li>
        <li><a href="arcani-maggiori-limperatrice.html">L'imperatrice</a></li>
        </ul>
    </li>
        <span class="opener">Arcani minori</span>
        <ul>
        <li>
            <span class="opener">Le Spade</span>
            <ul>
            <li><a href="arcani-minori-le-spade.html">Asso di Spade</a></li>
            <li><a href="arcani-minori-le-spade.html">Due di Spade</a></li>
        </ul>
        </li>   
    <li><a href="strumenti.html">Strumenti</a></li> 
</ul>
</nav>                      

This is the CSS code:

/* Menu */
#menu ul {
  -moz-user-select: none;
  -webkit-user-select: none;
  -ms-user-select: none;
  user-select: none;
  color: #3d4449;
  font-family: "Roboto Slab", serif;
  font-weight: 400;
  letter-spacing: 0.075em;
  list-style: none;
  margin-bottom: 0;
  padding: 0;
  text-transform: uppercase; }
  #menu ul a, #menu ul span {
    border-bottom: 0;
    color: inherit;
    cursor: pointer;
    display: block;
    font-size: 0.9em;
    padding: 0.625em 0; }
    #menu ul a:hover, #menu ul span:hover {
      color: #f56a6a; }
    #menu ul a.opener, #menu ul span.opener {
      -moz-transition: color 0.2s ease-in-out;
      -webkit-transition: color 0.2s ease-in-out;
      -ms-transition: color 0.2s ease-in-out;
      transition: color 0.2s ease-in-out;
      text-decoration: none;
      -webkit-tap-highlight-color: rgba(255, 255, 255, 0);
      position: relative; }
      #menu ul a.opener:before, #menu ul span.opener:before {
        -moz-osx-font-smoothing: grayscale;
        -webkit-font-smoothing: antialiased;
        display: inline-block;
        font-style: normal;
        font-variant: normal;
        text-rendering: auto;
        line-height: 1;
        text-transform: none !important;
        font-family: 'Font Awesome 5 Free';
        font-weight: 900; }
      #menu ul a.opener:before, #menu ul span.opener:before {
        -moz-transition: color 0.2s ease-in-out, -moz-transform 0.2s ease-in-out;
        -webkit-transition: color 0.2s ease-in-out, -webkit-transform 0.2s ease-in-out;
        -ms-transition: color 0.2s ease-in-out, -ms-transform 0.2s ease-in-out;
        transition: color 0.2s ease-in-out, transform 0.2s ease-in-out;
        color: #9fa3a6;
        content: '\f078';
        position: absolute;
        right: 0; }
      #menu ul a.opener:hover:before, #menu ul span.opener:hover:before {
        color: #f56a6a; }
      #menu ul a.opener.active + ul, #menu ul span.opener.active + ul {
        display: block; }
      #menu ul a.opener.active:before, #menu ul span.opener.active:before {
        -moz-transform: rotate(-180deg);
        -webkit-transform: rotate(-180deg);
        -ms-transform: rotate(-180deg);
        transform: rotate(-180deg); }

#menu > ul > li {
  border-top: solid 1px rgba(210, 215, 217, 0.75);
  margin: 0.5em 0 0 0;
  padding: 0.5em 0 0 0; }
  #menu > ul > li > ul{
    color: #9fa3a6;
    display: none;
    margin: 0.5em 0 1.5em 0;
    padding-left: 1em; }
    #menu > ul > li > ul a, #menu > ul > li > ul span {
      font-size: 0.8em; }
    #menu > ul > li > ul > li {
      margin: 0.125em 0 0 0;
      padding: 0.125em 0 0 0; }
  #menu > ul > li:first-child {
    border-top: 0;
    margin-top: 0;
    padding-top: 0; }

I’d like to do this how for the second level. Excuse me for my English, my native language is Italian.

Thanks very much for the help!

You are only hiding the second level by default because you used the child selector. To hide all nested uls just do this:

#menu > ul ul {display:none;}

Instead of this:

#menu > ul > li > ul {display:none;}

1 Like

You need to update the HTML and CSS. Here’s how:

HTML Changes

  1. Move the third-level ul inside the second-level li.
  2. Make sure the second-level li items that should have a third level also have the class opener.

Update your HTML like this:

<nav id="menu">
<header class="major">
<h2>Menu</h2>
</header>
<ul>
    <li><a href="index.html">Homepage</a></li>
    <li><a href="introduzione.html">Introduzione</a></li>
    <li>
        <span class="opener">Arcani maggiori</span>
        <ul>
            <li><a href="arcani-maggiori-il-matto.html">Il Matto</a></li>
            <li><a href="arcani-maggiori-il-mago.html">Il Mago</a></li>
            <li><a href="arcani-maggiori-la-papessa.html">La Papessa</a></li>
            <li><a href="arcani-maggiori-limperatrice.html">L'imperatrice</a></li>
        </ul>
    </li>
    <li>
        <span class="opener">Arcani minori</span>
        <ul>
            <li>
                <span class="opener">Le Spade</span>
                <ul>
                    <li><a href="arcani-minori-le-spade.html">Asso di Spade</a></li>
                    <li><a href="arcani-minori-le-spade.html">Due di Spade</a></li>
                </ul>
            </li>   
        </ul>
    </li>   
    <li><a href="strumenti.html">Strumenti</a></li> 
</ul>
</nav>

CSS Changes

  1. Update CSS to ensure third-level menus are styled similarly to the second level and are hidden by default.
  2. Add a rule to show the third-level menu when its parent li is active.

Update your CSS with these rules:

/* Styles for the third-level menu */
#menu ul ul ul {
  display: none; /* Hide third-level by default */
  padding-left: 1.5em; /* Indent third-level items */
}

#menu ul ul li {
  position: relative; /* Ensure proper positioning for third-level items */
}

#menu ul ul li .opener:before {
  content: '\f078'; /* Use Font Awesome icon */
  position: absolute;
  right: 0;
}

#menu ul ul li .opener.active:before {
  transform: rotate(-180deg); /* Rotate the icon when active */
}

/* Show third-level menu when its parent is active */
#menu ul ul li.active > ul {
  display: block;
}

JavaScript (for interaction)

You’ll need a bit of JavaScript to handle the click events and toggle the active class. Here’s a simple example:

document.querySelectorAll('#menu .opener').forEach(opener => {
  opener.addEventListener('click', function() {
    this.classList.toggle('active');
    const subMenu = this.nextElementSibling;
    if (subMenu && subMenu.tagName === 'UL') {
      subMenu.style.display = subMenu.style.display === 'block' ? 'none' : 'block';
    }
  });
});

This script will handle opening and closing of sub-menus. Add it to your page or a separate JavaScript file and make sure it’s included in your HTML.

With these changes, clicking on a second-level menu item will show its nested items, and clicking on a third-level item will do the same.

1 Like

I tried this, but the elements with this instriction are all opened.

As mentioned by @averyjonesss888 you have some mismatched html and there is a list pair missing. The correct html is this:

<nav id="menu">
  <header class="major">
    <h2>Menu</h2>
  </header>
  <ul>
    <li><a href="index.html">Homepage</a></li>
    <li><a href="introduzione.html">Introduzione</a></li>
    <li>
      <span class="opener">Arcani maggiori</span>
      <ul>
        <li><a href="arcani-maggiori-il-matto.html">Il Matto</a></li>
        <li><a href="arcani-maggiori-il-mago.html">Il Mago</a></li>
        <li><a href="arcani-maggiori-la-papessa.html">La Papessa</a></li>
        <li><a href="arcani-maggiori-limperatrice.html">L'imperatrice</a></li>
      </ul>
    </li>
    <li>
      <span class="opener">Arcani minori</span>
      <ul>
        <li>
          <span class="opener">Le Spade</span>
          <ul>
            <li><a href="arcani-minori-le-spade.html">Asso di Spade</a></li>
            <li><a href="arcani-minori-le-spade.html">Due di Spade</a></li>
          </ul>
        </li>
        <li><a href="strumenti.html">Strumenti</a></li>
      </ul>
    </li>
  </ul>
</nav>

Assuming you add the css I gave you and that you are adding and removing the active class correctly the menu will function as expected. You didn’t show the js were you using so if you are not removing the class from a nested menu properly it will still be open even if you closed the parent.

Here’s a rough codepen to show it working.

Needs tidying up as just typed that in on my mobile and haven’t had time to validate.

1 Like

This is the js code

// Menu.
		var $menu = $('#menu'),
			$menu_openers = $menu.children('ul').find('.opener');

		// Openers.
			$menu_openers.each(function() {

				var $this = $(this);

				$this.on('click', function(event) {

					// Prevent default.
						event.preventDefault();

					// Toggle.
						$menu_openers.not($this).removeClass('active');
						$this.toggleClass('active');

					// Trigger resize (sidebar lock).
						$window.triggerHandler('resize.sidebar-lock');

				});

			});

})(jQuery);

That code won’t work as it removes the active class from everything and then only applies it to the clicked item. That means that a parent of the ul has the active class removed and therefore all nested uls will also be hidden even if they have the newly added active class. The logic is completely broken. You need to remove all classes except for the current ‘tree’ of open menus and then you need to close all submenus when a parent submenu is closed.

I have given a simpler working example in vanilla js which you should use instead (except that it doesn’t close any previously opened menus outside its scope).

Note that you still need to change the html and use the css that I provided.

If you want a jquery version then you’ll have to wait for someone else to pop along and help :slight_smile:

Change the toggle part of the script to this:

 // Toggle.
    $menu_openers.not($this).removeClass("active");
    $this.toggleClass("active");
    var theParent = $this.closest(".has-sub");
    if (theParent.length) {
      $(theParent).prev().addClass("active");
    }

Then add a helper class to the ul that holds this extra submenu.

e.g.

 <li>
      <span class="opener">Arcani minori</span>
      <ul class="has-sub">
        <li>
          <span class="opener">Le Spade</span>
          <ul>

Full html:

<nav id="menu">
  <header class="major">
    <h2>Menu</h2>
  </header>
  <ul>
    <li><a href="index.html">Homepage</a></li>
    <li><a href="introduzione.html">Introduzione</a></li>
    <li>
      <span class="opener">Arcani maggiori</span>
      <ul>
        <li><a href="arcani-maggiori-il-matto.html">Il Matto</a></li>
        <li><a href="arcani-maggiori-il-mago.html">Il Mago</a></li>
        <li><a href="arcani-maggiori-la-papessa.html">La Papessa</a></li>
        <li><a href="arcani-maggiori-limperatrice.html">L'imperatrice</a></li>
      </ul>
    </li>
    <li>
      <span class="opener">Arcani minori</span>
      <ul class="has-sub">
        <li>
          <span class="opener">Le Spade</span>
          <ul>
            <li><a href="arcani-minori-le-spade.html">Asso di Spade</a></li>
            <li><a href="arcani-minori-le-spade.html">Due di Spade</a></li>
          </ul>
        </li>
        <li><a href="strumenti.html">Strumenti</a></li>
      </ul>
    </li>
  </ul>
</nav>

Full revised CSS:

/* Menu */
#menu ul {
  -moz-user-select: none;
  -webkit-user-select: none;
  -ms-user-select: none;
  user-select: none;
  color: #3d4449;
  font-family: "Roboto Slab", serif;
  font-weight: 400;
  letter-spacing: 0.075em;
  list-style: none;
  margin-bottom: 0;
  padding: 0;
  text-transform: uppercase;
}
#menu ul a,
#menu ul span {
  border-bottom: 0;
  color: inherit;
  cursor: pointer;
  display: block;
  font-size: 0.9em;
  padding: 0.625em 0;
}
#menu ul a:hover,
#menu ul span:hover {
  color: #f56a6a;
}
#menu ul a.opener,
#menu ul span.opener {
  transition: color 0.2s ease-in-out;
  text-decoration: none;
  -webkit-tap-highlight-color: rgba(255, 255, 255, 0);
  position: relative;
}
#menu ul a.opener:before,
#menu ul span.opener:before {
  -moz-osx-font-smoothing: grayscale;
  -webkit-font-smoothing: antialiased;
  display: inline-block;
  font-style: normal;
  font-variant: normal;
  text-rendering: auto;
  line-height: 1;
  text-transform: none !important;
  font-family: "Font Awesome 5 Free";
  font-weight: 900;
}
#menu ul a.opener:before,
#menu ul span.opener:before {
  transition: color 0.2s ease-in-out, transform 0.2s ease-in-out;
  color: #9fa3a6;
  content: "\f078";
  position: absolute;
  right: 0;
}
#menu ul a.opener:hover:before,
#menu ul span.opener:hover:before {
  color: #f56a6a;
}
#menu ul a.opener.active + ul,
#menu ul span.opener.active + ul {
  display: block;
}
#menu ul a.opener.active:before,
#menu ul span.opener.active:before {
  transform: rotate(-180deg);
}

#menu > ul > li {
  border-top: solid 1px rgba(210, 215, 217, 0.75);
  margin: 0.5em 0 0 0;
  padding: 0.5em 0 0 0;
}
#menu > ul ul {
  color: #9fa3a6;
  display: none;
  margin: 0.5em 0 1.5em 0;
  padding-left: 1em;
}
#menu > ul > li > ul a,
#menu > ul > li > ul span {
  font-size: 0.8em;
}
#menu > ul > li > ul > li {
  margin: 0.125em 0 0 0;
  padding: 0.125em 0 0 0;
}
#menu > ul > li:first-child {
  border-top: 0;
  margin-top: 0;
  padding-top: 0;
}

Working example:

1 Like

Thanks very much!! Now it works! Thanks at all for the help!

1 Like

Another question…
my website has many pages and sometimes I need to add a new link to the menu or change where one of the links points to. This means that on every page that the menu exist I must manually change the link. Is there some way to have a master menu that is just put on every page?

You can use server-side includes (SSI) or scripting languages like PHP to create a master menu file and include it on every page. This way, updates are made in one place and automatically applied everywhere. If you’re using a CMS, it likely has built-in menu management to handle this for you.

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.