You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过方向键在HTML的不同div区块间切换导航?

实现跨区块方向键导航方案

核心思路

通过状态变量跟踪当前激活的区块,针对每个区块的结构和导航需求分别处理方向键逻辑,同时重构选中状态切换函数以适配多区块场景。

关键修改点

  1. 状态扩展:新增currentSection记录当前所在区块,为每个区块维护独立的选中位置:

    • 粉色区块:保留行列坐标selectedPink
    • 红色区块:单元素仅标记选中状态
    • 绿色区块:用selectedGreen记录选中列序号
  2. 重构选中激活函数:ActivateSelection先清除所有区块的hover状态,再根据当前区块定位目标元素并添加hover类,确保选中状态唯一。

  3. 分区块按键处理:

    • 粉色区块:右键到最右侧时切换到红色区块;下键到最底部时切换到绿色区块并选中中间元素
    • 红色区块:左键直接切换回粉色区块的最后选中位置
    • 绿色区块:左右键仅在列范围内移动,上键切换回粉色区块
  4. 点击事件适配:点击任意区块的元素时,自动切换到对应区块并更新选中状态


完整实现代码

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 12:45:34