Bootstrap 5를 활용한 멀티 레벨 드롭다운 리스트 구현

  • 6 minutes to read

멀티 레벨 드롭다운 리스트는 웹사이트나 애플리케이션에서 복잡한 내비게이션 구조를 효율적으로 관리할 수 있게 해줍니다. Bootstrap 5와 함께 사용하면, 이러한 멀티 레벨 드롭다운 메뉴를 손쉽게 구현할 수 있습니다. 다음은 Bootstrap 5를 활용하여 멀티 레벨 드롭다운 리스트를 구현하는 방법을 단계별로 설명합니다.

CSS 스타일링

멀티 레벨 드롭다운 리스트의 기본적인 구조와 스타일을 정의합니다.

<style>
    .dropdown-submenu {
        position: relative; /* 상대적 위치 설정으로, 절대 위치를 가진 자식 요소들의 기준점을 설정합니다. */
    }

        .dropdown-submenu a::after {
            transform: rotate(-90deg); /* 가상 요소(대개 화살표)를 -90도 회전시킵니다. */
            position: absolute; /* 가상 요소를 상대적 위치를 가진 부모 요소 내에서 절대 위치로 설정합니다. */
            right: 6px; /* 부모 요소의 오른쪽 가장자리에서 6px 떨어진 곳에 위치시킵니다. */
            top: .8em; /* 부모 요소의 상단에서 .8em 떨어진 곳에 위치시킵니다. */
        }

        .dropdown-submenu .dropdown-menu {
            top: 0; /* 서브메뉴의 상단을 부모 메뉴의 상단과 맞춥니다. */
            left: 100%; /* 서브메뉴를 부모 메뉴의 바로 오른쪽에 위치시킵니다. */
            margin-left: .1rem; /* 부모 메뉴와의 간격을 위해 왼쪽 여백을 조금 추가합니다. */
            margin-right: .1rem; /* 일관된 간격을 위해 오른쪽 여백을 조금 추가합니다. */
        }
</style>

HTML 구조

HTML에서는 Bootstrap 클래스를 사용하여 드롭다운 메뉴를 구성합니다. 다음 예시는 멀티 레벨 드롭다운 메뉴를 구성하는 방법을 보여줍니다.

<header>
    <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
        <div class="container">
            <!-- Navbar Brand -->
            <a class="navbar-brand" href="#">Your Brand</a>
            <!-- Toggler/Collapsibe Button -->
            <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                    aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>
            <!-- Navbar links -->
            <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
                <ul class="navbar-nav flex-grow-1">
                    <!-- Single level Dropdown -->
                    <li class="nav-item dropdown">
                        <a class="nav-link dropdown-toggle text-dark" href="#" id="navbarBooks" role="button" data-bs-toggle="dropdown" aria-expanded="false">
                            Books
                        </a>
                        <ul class="dropdown-menu" aria-labelledby="navbarBooks">
                            <li><a class="dropdown-item text-dark" href="#">C# 교과서</a></li>
                            <li><a class="dropdown-item text-dark" href="#">ASP.NET & Core를 다루는 기술</a></li>
                            <li><hr class="dropdown-divider" /></li>
                            <li><a class="dropdown-item text-dark" href="#">Something else here</a></li>
                        </ul>
                    </li>
                    <!-- Multi-level Dropdown -->
                    <li class="nav-item dropdown">
                        <a class="nav-link dropdown-toggle text-dark" href="#" id="navbarCourses" role="button" data-bs-toggle="dropdown" aria-expanded="false">
                            Courses
                        </a>
                        <ul class="dropdown-menu" aria-labelledby="navbarCourses">
                            <!-- First level Dropdown -->
                            <li class="dropdown-submenu">
                                <a class="dropdown-item dropdown-toggle text-dark" href="#">DevLec</a>
                                <ul class="dropdown-menu">
                                    <li><a class="dropdown-item" href="#">C#</a></li>
                                    <li><a class="dropdown-item" href="#">ASP.NET Core</a></li>
                                </ul>
                            </li>
                            <!-- Nested level Dropdown -->
                            <li class="dropdown-submenu">
                                <a class="dropdown-item dropdown-toggle text-dark" href="#">VisualAcademy</a>
                                <ul class="dropdown-menu">
                                    <li><a class="dropdown-item" href="#">Youtube</a></li>
                                    <li class="dropdown-submenu">
                                        <a class="dropdown-item dropdown-toggle text-dark" href="#">JavaCampus</a>
                                        <ul class="dropdown-menu">
                                            <li><a class="dropdown-item" href="#">Java</a></li>
                                            <li><a class="dropdown-item" href="#">Spring Boot</a></li>
                                        </ul>
                                    </li>
                                </ul>
                            </li>
                        </ul>
                    </li>
                </ul>
                <!-- Additional elements like login or others can be added here -->
            </div>
        </div>
    </nav>
</header>

JavaScript 인터랙션

드롭다운 메뉴의 동작을 제어하기 위해 JavaScript를 사용합니다. 다음 스크립트는 드롭다운 요소의 클릭 이벤트를 처리하고, 서브 메뉴의 표시 상태를 토글합니다.

<script>
    // 문서가 완전히 로드되었을 때 스크립트가 실행되도록 합니다.
    document.addEventListener('DOMContentLoaded', function () {
        // 모든 드롭다운 토글 요소를 선택합니다.
        var dropdownToggleElements = document.querySelectorAll('.dropdown-submenu .dropdown-toggle');

        // 각 토글 요소에 대한 이벤트 리스너를 설정합니다.
        dropdownToggleElements.forEach(function (dropdownToggle) {
            dropdownToggle.addEventListener('click', function (event) {
                event.preventDefault(); // 클릭에 의한 기본 동작을 방지합니다.
                event.stopPropagation(); // 이벤트가 상위로 전파되는 것을 방지합니다.

                // 클릭된 토글 바로 다음에 오는 서브 메뉴를 선택합니다.
                var currentSubMenu = this.nextElementSibling;
                // 현재 서브 메뉴가 보이는 상태인지 확인합니다.
                var isCurrentlyShown = currentSubMenu.classList.contains('show');
                // 현재 토글의 상위 메뉴를 찾습니다.
                var parentMenu = this.parentElement.parentElement.closest('.dropdown-submenu');

                // 만약 상위 메뉴가 있고 현재 서브 메뉴가 열려 있다면, 서브 메뉴를 닫습니다.
                if (parentMenu && isCurrentlyShown) {
                    closeSubMenu(currentSubMenu);
                    return; // 추가 실행을 중단합니다.
                }

                // 상위 메뉴를 클릭했을 때, 다른 모든 서브 메뉴를 닫고 현재 메뉴의 상태를 토글합니다.
                if (!parentMenu) {
                    closeAllSubMenus(this); // 다른 모든 서브 메뉴를 닫습니다.
                    if (!isCurrentlyShown) {
                        currentSubMenu.classList.add('show'); // 현재 메뉴를 엽니다.
                    }
                } else {
                    // 하위 메뉴를 클릭했을 때 현재 메뉴의 상태에 따라 토글합니다.
                    if (!isCurrentlyShown) {
                        currentSubMenu.classList.add('show'); // 현재 메뉴를 엽니다.
                    } else {
                        closeSubMenu(currentSubMenu); // 현재 메뉴를 닫습니다.
                    }
                }
            });
        });

        // 지정된 요소를 제외한 모든 서브 메뉴를 닫는 함수입니다.
        function closeAllSubMenus(exceptThis) {
            document.querySelectorAll('.dropdown-submenu .dropdown-menu').forEach(function (menu) {
                if (!exceptThis.contains(menu)) {
                    closeSubMenu(menu); // 서브 메뉴를 닫습니다.
                }
            });
        }

        // 지정된 메뉴와 그 하위 메뉴를 닫는 함수입니다.
        function closeSubMenu(menu) {
            menu.classList.remove('show'); // 메뉴를 닫습니다.
            var subMenus = menu.querySelectorAll('.dropdown-menu');
            subMenus.forEach(function (subMenu) {
                subMenu.classList.remove('show'); // 하위 메뉴를 닫습니다.
            });
        }
    });
</script>

전체 소스 코드는 VisualAcademy 프로젝트를 참고하세요.

VisualAcademy Docs의 모든 콘텐츠, 이미지, 동영상의 저작권은 박용준에게 있습니다. 저작권법에 의해 보호를 받는 저작물이므로 무단 전재와 복제를 금합니다. 사이트의 콘텐츠를 복제하여 블로그, 웹사이트 등에 게시할 수 없습니다. 단, 링크와 SNS 공유, Youtube 동영상 공유는 허용합니다. www.VisualAcademy.com
박용준 강사의 모든 동영상 강의는 데브렉에서 독점으로 제공됩니다. www.devlec.com