如何通过方向键在HTML的不同div区块间切换导航?
实现跨区块方向键导航方案
核心思路
通过状态变量跟踪当前激活的区块,针对每个区块的结构和导航需求分别处理方向键逻辑,同时重构选中状态切换函数以适配多区块场景。
关键修改点
状态扩展:新增
currentSection记录当前所在区块,为每个区块维护独立的选中位置:- 粉色区块:保留行列坐标
selectedPink - 红色区块:单元素仅标记选中状态
- 绿色区块:用
selectedGreen记录选中列序号
- 粉色区块:保留行列坐标
重构选中激活函数:
ActivateSelection先清除所有区块的hover状态,再根据当前区块定位目标元素并添加hover类,确保选中状态唯一。分区块按键处理:
- 粉色区块:右键到最右侧时切换到红色区块;下键到最底部时切换到绿色区块并选中中间元素
- 红色区块:左键直接切换回粉色区块的最后选中位置
- 绿色区块:左右键仅在列范围内移动,上键切换回粉色区块
点击事件适配:点击任意区块的元素时,自动切换到对应区块并更新选中状态
完整实现代码
HTML(结构无需修改)
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous"> <div class="container-fluid h-100 d-flex flex-column bg-secondary text-white"> <div class="row flex-fill"> <div class="col-lg-6 row" id="tails" style="background-color: pink;"> <div class="m-auto d-flex justify-content-around p-3 boxGroup"> <div class="box"></div> <div class="box "></div> <div class="box"></div> </div> <div class=" m-auto d-flex justify-content-around p-3 boxGroup"> <div class="box"></div> <div class="box hover"></div> <div class="box"></div> </div> <div class=" m-auto d-flex justify-content-around p-3 boxGroup"> <div class="box"></div> <div class="box "></div> <div class="box"></div> </div> </div> <div class=" col-lg-6 d-flex justify-content-around " id="text" style="background-color: rgb(156, 51, 69);"> <div class="box m-auto"></div> </div> </div> <div class="row " id="navigation" style="background-color: rgb(142, 197, 71);"> <div class=" m-auto d-flex justify-content-around p-3"> <div class="box-navigation"></div> <div class="box-navigation"></div> <div class="box-navigation"></div> </div> </div> </div>
CSS(补充红色/绿色区块hover样式)
body, html { margin: 0; padding: 0; } * { box-sizing: border-box; } div { overflow: hidden; } .box { background-color: rgb(100, 12, 120); height: 100px; width: 100px; transition: transform .6s; } .box-navigation { background-color: rgb(100, 12, 120); height: 100px; width: 100px; transition: transform .6s; } .box-navigation:hover, .box:hover { transform: scale(1.1); background-color: rgb(20, 10, 3); } .hover { background-color: rgb(200, 150, 120); transform: scale(.9); } .active { background-color: rgb(200, 51, 200); transform: scale(1.2); } .flex-fill { flex: 1 1 auto; }
JavaScript(完整逻辑实现)
// 获取各区块元素 const pinkContainer = document.getElementById('tails'); const redBox = document.getElementById('text').querySelector('.box'); const greenContainer = document.getElementById('navigation').querySelector('.d-flex'); // 状态管理 let currentSection = 'pink'; // 当前激活区块:pink/red/green const selectedPink = { row: 2, column: 2 }; // 粉色区块选中位置 let selectedGreen = 2; // 绿色区块选中列(1-3) // 数值边界限制工具函数 const Clamp = (num, min, max) => Math.min(Math.max(num, min), max); // 清除所有区块的hover状态 const clearAllHover = () => { document.querySelectorAll('.hover').forEach(el => el.classList.remove('hover')); }; // 激活当前选中元素 const ActivateSelection = () => { clearAllHover(); switch(currentSection) { case 'pink': const pinkTarget = pinkContainer.querySelector(`.boxGroup:nth-child(${selectedPink.row}) .box:nth-child(${selectedPink.column})`); pinkTarget.classList.add('hover'); break; case 'red': redBox.classList.add('hover'); break; case 'green': const greenTarget = greenContainer.querySelector(`.box-navigation:nth-child(${selectedGreen})`); greenTarget.classList.add('hover'); break; } }; // 方向键事件处理 const OnKeyDown = event => { event.preventDefault(); // 阻止页面滚动默认行为 switch(currentSection) { case 'pink': const maxPinkRow = pinkContainer.querySelectorAll('.boxGroup').length; const maxPinkCol = pinkContainer.querySelectorAll(`.boxGroup:nth-child(${selectedPink.row}) .box`).length; switch(event.key) { case "ArrowUp": selectedPink.row = Clamp(selectedPink.row - 1, 1, maxPinkRow); break; case "ArrowDown": if (selectedPink.row === maxPinkRow) { // 切换到绿色区块 currentSection = 'green'; selectedGreen = 2; ActivateSelection(); return; } selectedPink.row = Clamp(selectedPink.row + 1, 1, maxPinkRow); break; case "ArrowRight": if (selectedPink.column === maxPinkCol) { // 切换到红色区块 currentSection = 'red'; ActivateSelection(); return; } selectedPink.column = Clamp(selectedPink.column + 1, 1, maxPinkCol); break; case "ArrowLeft": selectedPink.column = Clamp(selectedPink.column - 1, 1, maxPinkCol); break; case "Enter": const activePink = pinkContainer.querySelector(`.boxGroup:nth-child(${selectedPink.row}) .box:nth-child(${selectedPink.column})`); activePink.classList.add('active'); break; default: return; } ActivateSelection(); break; case 'red': switch(event.key) { case "ArrowLeft": currentSection = 'pink'; ActivateSelection(); break; case "Enter": redBox.classList.add('active'); break; default: return; } break; case 'green': const maxGreenCol = greenContainer.querySelectorAll('.box-navigation').length; const prevGreen = selectedGreen; switch(event.key) { case "ArrowUp": currentSection = 'pink'; ActivateSelection(); return; case "ArrowRight": selectedGreen = Clamp(selectedGreen + 1, 1, maxGreenCol); break; case "ArrowLeft": selectedGreen = Clamp(selectedGreen - 1, 1, maxGreenCol); break; case "Enter": const activeGreen = greenContainer.querySelector(`.box-navigation:nth-child(${selectedGreen})`); activeGreen.classList.add('active'); break; default: return; } if (prevGreen !== selectedGreen) ActivateSelection(); break; } }; // 点击事件绑定 pinkContainer.querySelectorAll('.box').forEach((box, index) => { const row = Math.floor(index / 3) + 1; const col = (index % 3) + 1; box.addEventListener('click', () => { currentSection = 'pink'; selectedPink.row = row; selectedPink.column = col; ActivateSelection(); }); }); redBox.addEventListener('click', () => { currentSection = 'red'; ActivateSelection(); }); greenContainer.querySelectorAll('.box-navigation').forEach((box, index) => { box.addEventListener('click', () => { currentSection = 'green'; selectedGreen = index + 1; ActivateSelection(); }); }); // 初始化激活状态 ActivateSelection(); window.addEventListener('keydown', OnKeyDown);
功能验证
- 页面加载时,粉色区块中间元素默认选中,可正常上下左右移动
- 粉色最右侧元素按右键 → 切换到红色区块;红色区块按左键 → 返回粉色区块
- 粉色最底部元素按下键 → 切换到绿色区块中间元素;绿色区块可左右移动,按上键返回粉色区块
- 所有区块按下Enter键可标记元素为active状态
内容的提问来源于stack exchange,提问作者Dmiich
相关产品推荐
相关产品推荐

