javascriptdrop-down-menutailwind-csssubmenu

Tailwind navbar with nested sub menus


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


Solution

  • 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.