如何用Vanilla JavaScript实现动态响应式Navbar?
自适应折叠导航栏(原生JS实现)
这种导航栏叫自适应折叠导航栏(Adaptive Collapsing Navbar),完全可以用原生JavaScript实现,核心逻辑是根据屏幕宽度动态调整可见链接数量,把超出的移入「更多」下拉菜单。直接上可运行的代码和思路:
核心逻辑
- 用Flex布局搭建导航栏基础结构,确保链接能横向排列
- 监听窗口
resize事件(加防抖避免频繁触发),计算导航栏的可用宽度 - 遍历所有导航链接,累加宽度,把超出可用宽度的链接移到下拉菜单;如果宽度足够,再把下拉里的链接移回原位置
- 控制「更多」按钮的显示/隐藏,没有待折叠链接时自动隐藏
HTML 结构
<nav class="navbar"> <div class="navbar-logo">我的网站</div> <div class="navbar-links" id="navbarLinks"> <a href="#" class="nav-link">链接1</a> <a href="#" class="nav-link">链接2</a> <a href="#" class="nav-link">链接3</a> <a href="#" class="nav-link">链接4</a> <a href="#" class="nav-link">链接5</a> <a href="#" class="nav-link">链接6</a> <a href="#" class="nav-link">链接7</a> <a href="#" class="nav-link">链接8</a> <a href="#" class="nav-link">链接9</a> </div> <div class="navbar-more"> <button class="more-btn" id="moreBtn">更多 ▾</button> <div class="more-dropdown" id="moreDropdown"> <!-- 动态移入的链接会放在这里 --> </div> </div> </nav>
CSS 样式
.navbar { display: flex; align-items: center; background: #333; padding: 0 1rem; overflow: hidden; } .navbar-logo { color: #fff; font-size: 1.2rem; margin-right: 2rem; white-space: nowrap; } .navbar-links { display: flex; gap: 1.5rem; margin-right: auto; } .nav-link { color: #fff; text-decoration: none; white-space: nowrap; padding: 0.5rem 0; transition: color 0.2s; } .nav-link:hover { color: #ccc; } .navbar-more { position: relative; display: none; /* 默认隐藏 */ } .more-btn { background: transparent; color: #fff; border: none; font-size: 1rem; cursor: pointer; padding: 0.5rem; } .more-dropdown { position: absolute; top: 100%; right: 0; background: #333; min-width: 120px; display: none; flex-direction: column; } .more-dropdown .nav-link { padding: 0.8rem 1rem; border-bottom: 1px solid #444; } /* 激活状态 */ .navbar-more.active { display: block; } .navbar-more.active .more-dropdown { display: flex; }
JavaScript 核心代码
// 获取DOM元素 const navbarLinks = document.getElementById('navbarLinks'); const moreDropdown = document.getElementById('moreDropdown'); const moreBtn = document.getElementById('moreBtn'); const navbarMore = document.querySelector('.navbar-more'); // 防抖函数:避免resize事件频繁触发计算 function debounce(func, delay = 200) { let timeoutId; return (...args) => { clearTimeout(timeoutId); timeoutId = setTimeout(() => func.apply(this, args), delay); }; } // 调整导航栏的核心函数 function adjustNavbar() { // 计算导航栏可用宽度:总宽度 - logo宽度 - 更多按钮宽度 - 预留边距 const navbarTotalWidth = document.querySelector('.navbar').offsetWidth; const logoWidth = document.querySelector('.navbar-logo').offsetWidth; const moreBtnWidth = moreBtn.offsetWidth; const availableWidth = navbarTotalWidth - logoWidth - moreBtnWidth - 40; // 40px是预留的左右边距 const links = Array.from(navbarLinks.children); let totalLinkWidth = 0; let linksToMove = []; // 遍历链接,计算总宽度,找出需要移入下拉的链接 links.forEach((link, index) => { // 累加链接宽度 + 链接之间的gap(对应CSS的gap:1.5rem=24px) totalLinkWidth += link.offsetWidth + 24; if (totalLinkWidth > availableWidth) { linksToMove.push(...links.slice(index)); } }); // 处理链接移动 if (linksToMove.length > 0) { linksToMove.forEach(link => moreDropdown.appendChild(link)); navbarMore.classList.add('active'); } else { // 宽度足够时,把下拉里的链接移回原位置 while (moreDropdown.firstChild) { navbarLinks.appendChild(moreDropdown.firstChild); } navbarMore.classList.remove('active'); } } // 页面加载时初始化一次 adjustNavbar(); // 监听窗口resize事件,用防抖优化 window.addEventListener('resize', debounce(adjustNavbar)); // 点击「更多」按钮切换下拉菜单 moreBtn.addEventListener('click', () => { moreDropdown.classList.toggle('active'); });
自定义调整提示
- 如果需要固定断点(比如屏幕宽度减少100px时显示7个链接),可以把动态计算逻辑改成判断
window.innerWidth,然后指定显示的链接数量 - 可以调整
availableWidth里的预留边距、链接的gap值,适配你的设计需求 - 用
element.getBoundingClientRect().width代替offsetWidth可以获得更精确的元素宽度(包含padding和border)
内容的提问来源于stack exchange,提问作者Nicik
相关产品推荐
相关产品推荐

