Flexbox下拉列表四向箭头导航功能实现求助
解决方案
核心逻辑
你的列表是Flex多行布局,要实现四向导航,关键是先算出每行能容纳的列表项数量,再根据箭头方向计算下一个要聚焦的项的索引。同时要处理无匹配项时的焦点回退逻辑。
完整代码实现
HTML(适配你的结构)
<div class="filter-container"> <input type="text" class="search-input" placeholder="搜索..." /> <button class="search-btn">搜索</button> <ul class="dropdown-list"> <!-- 动态生成的列表项,嵌套跳转链接 --> <li><a href="/item1">项目1</a></li> <li><a href="/item2">项目2</a></li> <li><a href="/item3">项目3</a></li> <li><a href="/item4">项目4</a></li> <li><a href="/item5">项目5</a></li> <li><a href="/item6">项目6</a></li> </ul> </div>
CSS(保留你的Flex多行布局)
.dropdown-list { display: flex; flex-wrap: wrap; list-style: none; padding: 0; margin: 0; max-width: 400px; /* 可根据需求调整容器宽度 */ } .dropdown-list li { flex: 0 0 calc(33.33% - 10px); /* 示例每行3个项,间距10px,按需修改 */ margin: 5px; } .dropdown-list a { display: block; padding: 8px; text-decoration: none; color: #333; border: 1px solid #ddd; border-radius: 4px; } .dropdown-list a:focus { outline: 2px solid #007bff; background-color: #f0f8ff; }
JavaScript(四向导航核心逻辑)
const searchBtn = document.querySelector('.search-btn'); const dropdownList = document.querySelector('.dropdown-list'); const searchInput = document.querySelector('.search-input'); // 全局监听键盘事件 document.addEventListener('keydown', (e) => { const activeElement = document.activeElement; const listItems = Array.from(dropdownList.querySelectorAll('a')); const hasItems = listItems.length > 0; // 无匹配项时,强制把焦点切回搜索按钮 if (!hasItems && activeElement.closest('.dropdown-list')) { searchBtn.focus(); return; } // 只处理聚焦在列表项或搜索按钮时的操作 if (!listItems.includes(activeElement) && activeElement !== searchBtn) return; // 阻止默认行为,避免页面滚动干扰 if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Enter'].includes(e.key)) { e.preventDefault(); } const currentIndex = listItems.indexOf(activeElement); // 计算每行项数:容器宽度除以单个项宽度,取整适配换行 const itemWidth = listItems[0]?.offsetWidth || 0; const containerWidth = dropdownList.offsetWidth; const itemsPerRow = itemWidth > 0 ? Math.floor(containerWidth / itemWidth) : 1; switch (e.key) { case 'ArrowRight': if (currentIndex < listItems.length - 1) { listItems[currentIndex + 1].focus(); } break; case 'ArrowLeft': if (currentIndex > 0) { listItems[currentIndex - 1].focus(); } break; case 'ArrowDown': const nextRowIndex = currentIndex + itemsPerRow; if (nextRowIndex < listItems.length) { listItems[nextRowIndex].focus(); } break; case 'ArrowUp': const prevRowIndex = currentIndex - itemsPerRow; if (prevRowIndex >= 0) { listItems[prevRowIndex].focus(); } break; case 'Enter': if (activeElement.tagName === 'A') { activeElement.click(); // 触发链接跳转 } else if (activeElement === searchBtn) { searchInput.focus(); // 搜索按钮回车时切回输入框,可自定义搜索逻辑 } break; } }); // 搜索按钮激活时,按箭头键直接切回搜索输入框(可选) searchBtn.addEventListener('keydown', (e) => { if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(e.key)) { e.preventDefault(); searchInput.focus(); } });
关键细节说明
- 自适应每行项数:通过容器宽度除以单个项宽度取整,适配不同屏幕下的Flex换行情况
- 焦点边界控制:所有箭头键都做了边界判断,不会超出列表项范围
- 无匹配项处理:列表为空时,任何聚焦在列表区域的操作都会自动切回搜索按钮
- 回车事件区分:列表项回车触发跳转,搜索按钮回车可自定义搜索逻辑
内容的提问来源于stack exchange,提问作者prixr
相关产品推荐
相关产品推荐

