Creating a Tree Menu in Bootstrap with Dropdown Submenus

Tree menus are a popular way to present hierarchical data in a structured manner. Using Bootstrap, you can create a stylish and responsive tree menu with dropdown submenus. In this tutorial, we'll walk through the process of creating a tree menu that works seamlessly across different devices, including mobile screens.

 

CSS Styling

First, we need to style our tree menu using CSS. This includes transitions, hover effects, and responsive design adjustments.

<style>
    .dropdown-menu li {
        transition: background-color 0.3s ease; /* Smooth transition */
    }

    .dropdown-menu li a {
        display: flex;
        align-items: center;
        justify-content: space-between;
        width: 100%;
    }

    .dropdown-menu li a:hover {
        background-color: #e3f2fd; /* Change background color on hover */
    }

    .dropdown-menu li:not(:hover) {
        transition: background-color 0.3s ease; /* Smooth transition */
    }

    .dropdown-menu li:not(:hover):hover {
        background-color: transparent; /* Reset background color on mouse out */
    }

    .dropdown-menu li a:visited {
        color: black; /* Change color to black for visited links */
    }

    .dropdown-menu li .menu-icon {
        float: right; /* Float the chevron to the right */
    }

    .dropdown-submenu {
        position: relative;
    }

    .dropdown-submenu .dropdown-menu {
        top: -9px;
        left: 100%;
        display: none; /* Hide submenus by default */
        position: absolute; /* Position submenus absolutely */
    }

    .dropdown-submenu:hover .dropdown-menu {
        display: block; /* Show submenu on hover */
    }

    .dropdown-submenu > .dropdown-toggle::after {
        content: none; /* Unicode for right chevron */
        float: right; /* Float the chevron to the right */
        margin-left: 0.5em; /* Adjust spacing */
    }

    @media (max-width: 767px) {
        .dropdown-menu {
            position: static !important; /* Make dropdown menus static for smaller screens */
            float: none;
        }

        .dropdown-submenu .dropdown-menu {
            position: static;
            margin-left: 1em; /* Indent submenu for visibility */
        }
    }
</style>

 

HTML Structure

Next, we'll create the HTML structure for our tree menu. This includes a main dropdown and a nested submenu.

<li class="nav-item dropdown">
    <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
        Administrator
    </a>
    <ul class="dropdown-menu" aria-labelledby="navbarDropdown">
        <li class="dropdown-submenu">
            <a class="dropdown-item dropdown-toggle" href="#">Submenu 1 <i class="fas fa-chevron-right menu-icon"></i></a>
            <ul class="dropdown-menu">
                <li><a class="dropdown-item" href="#">Submenu 1.1</a></li>
                <li><a class="dropdown-item" href="#">Submenu 1.2</a></li>
            </ul>
        </li>
        <li><a class="dropdown-item" href="#">Profile</a></li>
        <li><hr class="dropdown-divider"></li>
        <li><a class="dropdown-item" href="#">Logout</a></li>
    </ul>
</li>

 

JavaScript for Toggling Submenus

Lastly, we need to add JavaScript to handle the toggling of submenus when they are clicked. This script ensures the submenus remain open when clicked, especially on mobile devices where hover actions are not possible.

<script>
    document.addEventListener('DOMContentLoaded', function() {
        var dropdownSubmenus = document.querySelectorAll('.dropdown-submenu > .dropdown-item');

        dropdownSubmenus.forEach(function(submenu) {
            submenu.addEventListener('click', function(e) {
                var nextEl = this.nextElementSibling;
                if(nextEl && nextEl.classList.contains('dropdown-menu')) {
                    e.preventDefault();
                    e.stopPropagation(); // Prevent the dropdown from closing
                    if(nextEl.style.display == 'block') {
                        nextEl.style.display = 'none';
                    } else {
                        nextEl.style.display = 'block';
                    }
                }
            });
        });
    });
</script>

 

This is the result example.