1

Following the below tutorial I can see how to use a Tailwind navbar to code a menu, with a single sub-menu: https://webcrunch.com/posts/code-a-mega-menu-with-tailwind-css

Link to example via CodePen below: https://codepen.io/webcrunchblog/pen/MWxGMdR?editors=1010

Code snippet:

document.addEventListener("DOMContentLoaded", () => {
  // Select all dropdown toggle buttons
  const dropdownToggles = document.querySelectorAll(".dropdown-toggle")

  dropdownToggles.forEach((toggle) => {
    toggle.addEventListener("click", () => {
      // Find the next sibling element which is the dropdown menu
      const dropdownMenu = toggle.nextElementSibling

      // Toggle the 'hidden' class to show or hide the dropdown menu
      if (dropdownMenu.classList.contains("hidden")) {
        // Hide any open dropdown menus before showing the new one
        document.querySelectorAll(".dropdown-menu").forEach((menu) => {
          menu.classList.add("hidden")
        })

        dropdownMenu.classList.remove("hidden")
      } else {
        dropdownMenu.classList.add("hidden")
      }
    })
  })

  // Clicking outside of an open dropdown menu closes it
  window.addEventListener("click", function (e) {
    if (!e.target.matches(".dropdown-toggle")) {
      document.querySelectorAll(".dropdown-menu").forEach((menu) => {
        if (!menu.contains(e.target)) {
          menu.classList.add("hidden")
        }
      })
    }
  })
  
  // Mobile menu toggle
  
  const mobileMenuButton = document.querySelector('.mobile-menu-button')
  const mobileMenu = document.querySelector('.navigation-menu')
  
  mobileMenuButton.addEventListener('click', () => {
    mobileMenu.classList.toggle('hidden')
  })
  
  
})
<script src="https://cdn.tailwindcss.com"></script>
​<nav class="bg-sky-600 text-white">
  <div class="container mx-auto px-4 md:flex items-center gap-6">
    <!-- Logo -->
    <div class="flex items-center justify-between md:w-auto w-full">
      <a href="#" class="py-5 px-2 text-white flex-1 font-bold">Webcrunch.com</a>

      <!-- mobile menu icon -->
      <div class="md:hidden flex items-center">
        <button type="button" class="mobile-menu-button">
          <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
            <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25H12" />
          </svg>
        </button>
      </div>
    </div>

    <div class="hidden md:flex md:flex-row flex-col items-center justify-start md:space-x-1 pb-3 md:pb-0 navigation-menu">
      <a href="#" class="py-2 px-3 block">Home</a>
      <a href="#" class="py-2 px-3 block">About</a>
      <!-- Dropdown menu -->
      <div class="relative">
        <button type="button" class="dropdown-toggle py-2 px-3 hover:bg-sky-800 flex items-center gap-2 rounded">
          <span class="pointer-events-none select-none">Services</span>
          <svg class="w-3 h-3 pointer-events-none" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
            <path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
          </svg>
        </button>
        <div class="dropdown-menu absolute hidden bg-sky-700 text-white rounded-b-lg pb-2 w-48">
          <a href="#" class="block px-6 py-2 hover:bg-sky-800">Web Design</a>
          <a href="#" class="block px-6 py-2 hover:bg-sky-800">Web Development</a>
          <a href="#" class="block px-6 py-2 hover:bg-sky-800">SEO</a>
                <!-- Dropdown menu -->
      <div class="relative">
        <button type="button" class="dropdown-toggle py-2 px-3 hover:bg-sky-800 flex items-center gap-2 rounded">
          <span class="pointer-events-none select-none">Test</span>
          <svg class="w-3 h-3 pointer-events-none" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
            <path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
          </svg>
        </button>
        <div class="dropdown-menu absolute hidden bg-sky-700 text-white rounded-b-lg pb-2 w-48">
          <a href="#" class="block px-6 py-2 hover:bg-sky-800">Nested sub menu item</a>
        </div>
      </div>
        </div>
      </div>
      <a href="#" class="py-2 px-3 block">Contact</a>
    </div>
  </div>
</nav>

How can I modify this code to create a nested sub-menu. For example, trigger an additional sub-menu when clicking on a link within the existing sub-menu.

Attempts to do this by modifying the HTML results in the mainsub-menu closing when trying to trigger the nested sub-menu.

An example of the desired outcome is below: enter image description here

1 Answer 1

1

First of all, you have two attributes class on the two last svg elements.

Next, you need to close dropdowns if, and only if, they are not ancestors of the action button.

I've added a little of css with tailwind to beautify your tets button.

document.addEventListener("DOMContentLoaded", () => {
    // Select all dropdown toggle buttons
    const dropdownToggles = document.querySelectorAll(".dropdown-toggle")

    dropdownToggles.forEach((toggle) => {
        toggle.addEventListener("click", () => {
            // Find the next sibling element which is the dropdown menu
            const dropdownMenu = toggle.nextElementSibling

            // Toggle the 'hidden' class to show or hide the dropdown menu
            if (dropdownMenu.classList.contains("hidden")) {

                // Hide dropdown menus which are not ancestor of toggle before showing the new one
                document.querySelectorAll(".dropdown-menu").forEach((menu) => {
                    if (!menu.contains(toggle)) {
                        menu.classList.add("hidden")
                    }
                })

                dropdownMenu.classList.remove("hidden")
            } else {
                dropdownMenu.classList.add("hidden")
            }
        })
    })

    // Clicking outside of an open dropdown menu closes it
    window.addEventListener("click", function (e) {
        if (!e.target.matches(".dropdown-toggle")) {
            document.querySelectorAll(".dropdown-menu").forEach((menu) => {
                if (!menu.contains(e.target)) {
                    menu.classList.add("hidden")
                }
            })
        }
    })

    // Mobile menu toggle

    const mobileMenuButton = document.querySelector('.mobile-menu-button')
    const mobileMenu = document.querySelector('.navigation-menu')

    mobileMenuButton.addEventListener('click', () => {
        mobileMenu.classList.toggle('hidden')
    })


})
<script src="https://cdn.tailwindcss.com"></script>
<nav class="bg-sky-600 text-white">
    <div class="container mx-auto px-4 md:flex items-center gap-6">
        <!-- Logo -->
        <div class="flex items-center justify-between md:w-auto w-full">
            <a href="#" class="py-5 px-2 text-white flex-1 font-bold">Webcrunch.com</a>

            <!-- mobile menu icon -->
            <div class="md:hidden flex items-center">
                <button type="button" class="mobile-menu-button">
                    <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
                        <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25H12" />
                    </svg>
                </button>
            </div>
        </div>

        <div class="hidden md:flex md:flex-row flex-col items-center justify-start md:space-x-1 pb-3 md:pb-0 navigation-menu">
            <a href="#" class="py-2 px-3 block">Home</a>
            <a href="#" class="py-2 px-3 block">About</a>
            <!-- Dropdown menu -->
            <div class="relative">
                <button type="button" class="dropdown-toggle py-2 px-3 hover:bg-sky-800 flex items-center gap-2 rounded">
                    <span class="pointer-events-none select-none">Services</span>
                    <svg class="w-6 h-6 pointer-events-none" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
                        <path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
                    </svg>
                </button>
                <div class="dropdown-menu absolute hidden bg-sky-700 text-white rounded-b-lg pb-2 w-48">
                    <a href="#" class="block px-6 py-2 hover:bg-sky-800">Web Design</a>
                    <a href="#" class="block px-6 py-2 hover:bg-sky-800">Web Development</a>
                    <a href="#" class="block px-6 py-2 hover:bg-sky-800">SEO</a>
                    <!-- Dropdown menu -->
                    <div class="relative">
                        <button type="button" class="flex px-6 py-2 w-full justify-between dropdown-toggle py-2 px-3 hover:bg-sky-800 flex items-center gap-2 rounded">
                            <span class="pointer-events-none select-none">Test</span>
                            <svg class="w-3 h-3 pointer-events-none" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
                                <path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
                            </svg>
                        </button>
                        <div class="dropdown-menu absolute hidden bg-sky-700 text-white rounded-b-lg pb-2 w-48">
                            <a href="#" class="block px-6 py-2 hover:bg-sky-800">Nested sub menu item</a>
                        </div>
                    </div>
                </div>
            </div>
            <a href="#" class="py-2 px-3 block">Contact</a>
        </div>
    </div>
</nav>

Let me know if it doesn't suit you.

Sign up to request clarification or add additional context in comments.

2 Comments

This is working well, thank you. How can we rotate the dropdown menu arrow icon when a sub menu is open? I know this can be done in Tailwind using rotate-180.
Using rotate-180 is a good idea. You have to add it if the menu is hidden : toggle.querySelector('svg').classList.add('rotate-180') . If not, remove it : toggle.querySelector('svg').classList.remove('rotate-180') . And remove after the instruction menu.classList.add("hidden") : menu.previousElementSibling.querySelector('svg').classList.remove('rotate-180')

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.