使用JavaScript为动态构建的多级导航菜单项及所有父级添加active类
动态导航菜单层级Active类适配方案
需求说明
- 点击动态构建的导航菜单项时,为当前项及其所有上级父菜单(直至id为
home_li的顶级菜单)添加active类 - 导航由PHP从数据库动态生成,仅用JavaScript实现
- 原代码仅支持固定3级菜单,需改用循环适配任意层级
现有代码
HTML结构
<nav id="nav-bar" class="navbar navbar-expand-lg navbar-dark bg-dark bg-gradient px-lg-3 py-lg-2 fixed-top d-print-none"> <div class="container-fluid align-items-center"> <div class=" col-md-1 col-sm-1 col-lg-auto text-end"> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#main_nav" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> </div> <div class="collapse navbar-collapse" id="main_nav"> <!--Home Nav--> <ul class="nav navbar-nav ms-3" id="main_ul"> <li id="home_li" class="me-4"><a class="nav-link active btn btn-dark bg-gradient border border-1 border-white" href="http://index.php" id="active">Home</a></li> <li id="home_li" class="nav-item dropdown me-4"><a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" data-bs-auto-close="outside">Admin</a></li> <li id="home_li" class="nav-item dropdown me-4"><a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" data-bs-auto-close="outside" aria-expanded="false">Dropdown Section-1</a> <ul class="dropdown-menu shadow"> <li class="dropend"><a class="dropdown-item dropdown-toggle" data-bs-toggle="dropdown" data-bs-auto-close="outside">Dropdown Section-1-1</a> <ul class="dropdown-menu shadow"> <li><a class="dropdown-item" href="http://Page-1-1-1.php">Page-1-1-1</a></li> <li><a class="dropdown-item" href="http://Page-1-1-2.php">Page-1-1-2</a></li> <li><a class="dropdown-item" href="http://Page-1-1-3.php">Page-1-1-3</a></li> <li><a class="dropdown-item" href="http://Page-1-1-4.php">Page-1-1-4</a></li> <li><a class="dropdown-item" href="http://Page-1-1-5.php">Page-1-1-5</a></li> </ul> </li> <li class="dropend"><a class="dropdown-item dropdown-toggle" data-bs-toggle="dropdown" data-bs-auto-close="outside">Dropdown Section-1-2</a> <ul class="dropdown-menu shadow"> <li><a class="dropdown-item" href="http://Page-1-2-1.php">Page-1-2-1</a></li> <li><a class="dropdown-item" href="http://Page-1-2-2.php">Page-1-2-2</a></li> <li><a class="dropdown-item" href="http://Page-1-2-3.php">Page-1-2-3</a></li> </ul> </li> <li class="dropend"><a class="dropdown-item dropdown-toggle" data-bs-toggle="dropdown" data-bs-auto-close="outside">Dropdown Section-1-3</a> <ul class="dropdown-menu shadow"> <li><a class="dropdown-item" href="http://Page-1-3-1.php">Page-1-3-1</a></li> <li><a class="dropdown-item" href="http://Page-1-3-2.php">Page-1-3-2</a></li> <li><a class="dropdown-item" href="http://Page-1-3-3.php">Page-1-3-3</a></li> </ul> </li> <li><a class="dropdown-item" href="http://Page-1-1.php">Page-1-1</a></li> <li><a class="dropdown-item" href="http://Page-1-2.php">Page-1-2</a></li> </ul> </li> <li id="home_li" class="nav-item dropdown me-4"><a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" data-bs-auto-close="outside">Dropdown Section-2</a></li> <li id="home_li" class="nav-item dropdown me-4"><a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" data-bs-auto-close="outside">Dropdown Section-3</a></li> </ul> </div> </div> </nav>
原有JavaScript代码
var activePage = window.location.pathname; var navLinks = document.querySelectorAll('nav ul li a').forEach(link => { if (link.href.includes(`${activePage}`)) { link.id = 'active'; var a = document.getElementById('active'); var p = a.parentElement; var p1Id = p.getAttribute('id'); if (p1Id == 'home_li') { link.classList.add('active', 'btn', 'btn-dark', 'bg-gradient', 'border', 'border-1', 'border-white'); } else if (p1Id != 'home_li') { var p1 = p.parentElement; var p2 = p1.parentElement; var p2Id = p2.getAttribute('id'); if (p2Id == 'home_li') { link.classList.add('active'); var p2child = p2.children; p2child[0].classList.add('active'); p2child[0].classList.add('btn', 'btn-dark', 'bg-gradient', 'border', 'border-1', 'border-white'); } else if (p2Id != 'home_li') { var p3 = p2.parentElement; var p4 = p3.parentElement; var p4Id = p4.getAttribute('id'); if (p4Id == 'home_li') { link.classList.add('active'); var p2child = p2.children; var p3child = p4.children; p2child[0].classList.add('active'); p3child[0].classList.add('active'); p3child[0].classList.add('btn', 'btn-dark', 'bg-gradient', 'border', 'border-1', 'border-white'); } else if (p4Id != 'home_li') { var p5 = p4.parentElement; var p6 = p5.parentElement; var p6Id = p6.getAttribute('id'); if (p6Id == 'home_li') { link.classList.add('active'); var p2child = p2.children; var p3child = p4.children; var p4child = p6.children; p2child[0].classList.add('active'); p3child[0].classList.add('active'); p4child[0].classList.add('active'); p4child[0].classList.add('btn', 'btn-dark', 'bg-gradient', 'border', 'border-1', 'border-white'); } else if (p6Id != 'home_li') { var p7 = p6.parentElement; var p8 = p7.parentElement; var p8Id = p8.getAttribute('id'); if (p8Id == 'home_li') { link.classList.add('active'); var p2child = p2.children; var p3child = p4.children; var p4child = p6.children; var p5child = p8.children; p2child[0].classList.add('active'); p3child[0].classList.add('active'); p4child[0].classList.add('active'); p5child[0].classList.add('active'); p5child[0].classList.add('btn', 'btn-dark', 'bg-gradient', 'border', 'border-1', 'border-white'); } else { link.classList.add('active'); } } } } } } });
优化后的JavaScript代码
// 页面加载时匹配当前路径设置active function setActiveByPath() { const activePage = window.location.pathname; const navLinks = document.querySelectorAll('nav ul li a'); navLinks.forEach(link => { if (link.href.includes(activePage)) { // 重置所有active状态 resetAllActive(); // 为当前项及上级菜单添加active addActiveToHierarchy(link); } }); } // 点击菜单项时设置active function setupClickListeners() { const navLinks = document.querySelectorAll('nav ul li a'); navLinks.forEach(link => { link.addEventListener('click', function(e) { // 重置所有active状态 resetAllActive(); // 为点击项及上级菜单添加active addActiveToHierarchy(this); }); }); } // 为当前元素及其所有上级(直到home_li)添加active function addActiveToHierarchy(element) { let currentElement = element; let topLevelLink = null; // 向上遍历直到找到home_li while (currentElement) { // 添加active类 currentElement.classList.add('active'); // 记录顶级菜单的链接(即home_li下的a标签) const parentLi = currentElement.closest('li'); if (parentLi && parentLi.id === 'home_li') { topLevelLink = currentElement; break; } // 找到父级菜单的链接(当前li的父ul对应的li下的a标签) const parentUl = parentLi?.parentElement; if (parentUl?.tagName === 'UL') { currentElement = parentUl.closest('li')?.querySelector('a'); } else { break; } } // 为顶级链接添加额外样式 if (topLevelLink) { topLevelLink.classList.add('btn', 'btn-dark', 'bg-gradient', 'border', 'border-1', 'border-white'); } } // 重置所有导航项的active状态 function resetAllActive() { const allActiveLinks = document.querySelectorAll('nav ul li a.active'); allActiveLinks.forEach(link => { link.classList.remove('active', 'btn', 'btn-dark', 'bg-gradient', 'border', 'border-1', 'border-white'); }); } // 初始化 document.addEventListener('DOMContentLoaded', function() { setActiveByPath(); setupClickListeners(); });
优化说明
- 循环替代嵌套判断:使用
while循环向上遍历DOM树,适配任意层级的菜单结构,不再受固定层级限制 - 分离功能函数:将路径匹配、点击监听、层级设置、状态重置拆分为独立函数,代码更易维护
- 添加点击事件支持:实现点击菜单项时动态设置active类,满足交互需求
- 状态重置:每次设置active前先清除所有已有状态,避免冲突
- 精准样式控制:仅为顶级
home_li下的链接添加额外按钮样式,保持样式一致性
内容的提问来源于stack exchange,提问作者Srikanth
相关产品推荐
相关产品推荐

