如何实现类似Valorant官网的响应式头部导航(搜索框适配问题)
复刻Valorant导航的问题解决方案
核心思路
原来的逻辑只计算窗口宽度和右侧固定元素(语言图标、Play Now按钮)的宽度,现在得把搜索框展开后的宽度也纳入导航栏的空间计算。这样当搜索框打开时,只要导航栏总宽度超过窗口宽度,就优先把导航项移去MORE菜单,而不是挤压右侧的元素。
具体改法
跟踪搜索框的开关状态
- 加个变量
isSearchOpen记录搜索框是否展开,点击搜索图标时切换这个状态,同时给搜索容器加个类(比如.search-open),方便后续计算宽度。
- 加个变量
修改导航项移入移出的判断逻辑
- 计算导航栏可用于放置导航项的宽度时,要减去右侧固定元素的宽度,再减去搜索框展开后的宽度(如果搜索框处于打开状态)。
- 当显示的导航项总宽度超过这个可用宽度时,把最右侧的项移去MORE菜单;反过来,当有剩余空间时,把MORE里的项移回导航栏。
关键代码示例
JS部分:
let isSearchOpen = false; const searchIcon = document.querySelector('.search-icon'); const searchContainer = document.querySelector('.search-container'); const navItems = document.querySelectorAll('.nav-item'); const moreMenu = document.querySelector('.more-menu'); const rightElements = document.querySelector('.right-elements'); // 包含语言图标和Play Now的容器 // 点击搜索图标切换状态,同时调整导航项 searchIcon.addEventListener('click', () => { isSearchOpen = !isSearchOpen; searchContainer.classList.toggle('active', isSearchOpen); adjustNavItems(); }); // 窗口大小变化时自动调整导航项 window.addEventListener('resize', adjustNavItems); // 调整导航项的核心函数 function adjustNavItems() { const navBarTotalWidth = document.querySelector('.nav-bar').offsetWidth; const rightElementsWidth = rightElements.offsetWidth; // 搜索框展开时加入它的宽度,否则加0 const searchOccupiedWidth = isSearchOpen ? searchContainer.offsetWidth : 0; // 预留10px间距避免元素挤在一起 const availableWidthForNav = navBarTotalWidth - rightElementsWidth - searchOccupiedWidth - 10; let currentNavItemsTotalWidth = 0; const visibleItems = []; const hiddenItems = []; // 先统计当前显示的导航项总宽度 navItems.forEach(item => { if (!item.classList.contains('hidden')) { currentNavItemsTotalWidth += item.offsetWidth; visibleItems.push(item); } else { hiddenItems.push(item); } }); // 宽度超了,把最后一个可见项移去MORE菜单 while (currentNavItemsTotalWidth > availableWidthForNav && visibleItems.length > 0) { const lastItem = visibleItems.pop(); lastItem.classList.add('hidden'); hiddenItems.unshift(lastItem); currentNavItemsTotalWidth -= lastItem.offsetWidth; moreMenu.appendChild(lastItem); } // 有多余空间,把MORE里的项移回导航栏 while (currentNavItemsTotalWidth + (hiddenItems[0]?.offsetWidth || 0) <= availableWidthForNav && hiddenItems.length > 0) { const firstHiddenItem = hiddenItems.shift(); firstHiddenItem.classList.remove('hidden'); visibleItems.push(firstHiddenItem); currentNavItemsTotalWidth += firstHiddenItem.offsetWidth; // 将项插回MORE按钮的前面 const moreBtn = document.querySelector('.more-btn'); moreBtn.before(firstHiddenItem); } } // 页面加载时先执行一次调整 adjustNavItems();CSS部分(固定搜索框展开宽度,防止动态挤压):
.search-container { width: 0; overflow: hidden; transition: width 0.3s ease; } .search-container.active { width: 200px; /* 参考官网搜索框宽度,可自行调整 */ }
额外提醒
- 计算元素宽度时,可以用
getBoundingClientRect()替代offsetWidth,能更精准地获取元素实际占用的宽度(包括padding、border)。 - 记得给MORE菜单写好样式,确保移入的导航项能正常显示。
- 多测试不同窗口尺寸下的表现,尤其是搜索框开关时的过渡效果,保证交互流畅不卡顿。
内容的提问来源于stack exchange,提问作者Adrian
相关产品推荐
相关产品推荐

