键盘方向键移动当前选中的table单元格,并可以输入内容

news2024/12/24 11:00:59

有类似于这样的表格,用的<table>标签。原本要在单元格的文本框里面输入内容,需要用鼠标一个一个去点以获取焦点,现在需要不用鼠标选中,直接用键盘的上下左右来移动当前正在输入的单元格文本框。

 const currentCell = React.useRef<HTMLElement | null>() // 储存当前选中的单元格

 const  handleArrowKeys = (event) => { // 当按下键盘方向键做的事情
    if (!currentCell || !currentCell.current) return;
    const cellIndex = currentCell?.current?.cellIndex;
    let newCell;

    switch (event.key) {
      case 'ArrowUp':
        newCell = currentCell.current?.parentElement?.previousElementSibling?.cells[cellIndex];
        break;
      case 'ArrowDown':
        newCell = currentCell.current?.parentElement?.nextElementSibling?.cells[cellIndex];
        break;
      case 'ArrowLeft':
        newCell = currentCell?.current?.previousElementSibling;
        break;
      case 'ArrowRight':
        newCell = currentCell?.current?.nextElementSibling;
        break;
      default:
        break;
    }

    if (newCell) {
      if(currentCell?.current){
        currentCell.current.style.border = 'solid 2px black'
        // currentCell.current.style.boxShadow = 'none'
      }
      currentCell.current = newCell
      newCell.style.border = '3px solid #1890ff'
      // newCell.style.borderColor = '#1890ff'
      // newCell.style.boxShadow = '0 0 10px 5px #1890ff'
    }
  }

  useEffect(()=>{
      // 鼠标点击事件,因为第一次选中单元格肯定是要点击
      document.addEventListener("click", (e: MouseEvent) => {
      const target = e.target as HTMLElement

      // console.log(target.tagName, 'target')
      // 这里要判断被点击的对象是不是你需要监听的表格的单元格
      const isActive = (target.tagName === 'TD' || target.tagName === 'TH') && ...

      if (isActive) {
        if(currentCell?.current){ // 将原本被选中的单元格样式改为正常样式
          currentCell.current.style.border = 'solid 2px black'
        }
        // 新的单元格存起来,并高亮显示
        currentCell.current = target
        target.style.border = '3px solid #1890ff'
      } else {
        // 如果被点击的不是需要监听的地方,则整个表格“失去焦点”
        if(currentCell?.current){
          currentCell.current.style.border = 'solid 2px black'
          currentCell.current = null
        }
      }
    })
    document.addEventListener('keydown', function(e) {
      // console.log(e, 'e')
      if (e.ctrlKey || e.altKey){
        // 这是按ctrl+  alt+的情况,很多快捷键方式不知道怎么模仿。
      } else if (e.key === 'ArrowUp' || e.key === 'ArrowDown' || e.key === 'ArrowLeft' || e.key === 'ArrowRight') {
        handleArrowKeys(e);
      } else if (
        e.key === 'Insert' ||  // 按下的是插入键
        e.key === 'Home' ||  // 按下的是Home键
        e.key === 'End'  // 按下的是End键
        // 其他需要处理的按键
      ) {
        return
      } else{
        if(!currentCell || !currentCell.current) return
        let childNodes = currentCell.current.childNodes
        let inputIndex: any = null, textAreaIndex: any = null
        childNodes.forEach((node, index) => {
          if(node.tagName === 'INPUT') inputIndex = index
          if(node.tagName === 'TEXTAREA') textAreaIndex = index
        })
        if(inputIndex !== null){
          if (
            e.key === 'Backspace' ||  // 按下的是退格键
            e.key === 'Delete'
            // 其他需要处理的按键
          ) {
            childNodes[inputIndex].value = ''
          } else if(e.key.length === 1) {
            childNodes[inputIndex].value = childNodes[inputIndex].value + e.key
          }
        }else if(textAreaIndex !== null){
          if (
            e.key === 'Backspace' ||  // 按下的是退格键
            e.key === 'Delete'
            // 其他需要处理的按键
          ) {
            childNodes[textAreaIndex].value = ''
          } else if(e.key.length === 1) {
            childNodes[textAreaIndex].value = childNodes[inputIndex].value + e.key
          }
        }
      }
    });
  },[])

这种方式,实现的功能就是点击单元格,注意不能点击到格里的文本框(因为我觉得文本框都是单击它就获取了焦点,键盘方向键也是用来控制光标位置的,这里没有过多的去纠结去探究,也许可以做到),然后键盘的上下左右就能控制当前选中的单元格,输入,就能改变单元格的文本框的值。其实这样我觉得就和excel单击单元格选中,输入就是覆盖整个内容,方向键控制选中单元格;双击单元格才是继续编辑单元格内容,方向键控制光标差不多,不过我这个变成了单击单元格是选中,然后输入覆盖,单击文本框是继续输入。

但是,这样是有弊端的,代码中也能看出来,对于ctrl+,alt+这些快捷键的功能我没有模仿出来,可能跟个人能力有关,而且就算有办法我觉得可能也太复杂了(不想折腾),还有就是很重要的一点,他没办法输入中文,因为我是监听键盘按下的事件,然后获得它的key,那用户想输入中文,我也只能获取到一个一个的英文字母(本人也想过偷懒,因为这个系统这里的表格大多数是不用输入中文,少数有中文,后面闲着没事,就问了chat gpt得到一些灵感)。

 const currentCell = React.useRef<HTMLElement | null>() // 储存当前选中的单元格

 const  handleArrowKeys = (event) => { // 当按下键盘方向键做的事情
    if (!currentCell || !currentCell.current) return;
    const cellIndex = currentCell?.current?.cellIndex;
    let newCell;

    switch (event.key) {
      case 'ArrowUp':
        newCell = 
 currentCell.current?.parentElement?.previousElementSibling?.cells[cellIndex];
        break;
      case 'ArrowDown':
        newCell = 
 currentCell.current?.parentElement?.nextElementSibling?.cells[cellIndex];
        break;
      case 'ArrowLeft':
        newCell = currentCell?.current?.previousElementSibling;
        break;
      case 'ArrowRight':
        newCell = currentCell?.current?.nextElementSibling;
        break;
      default:
        break;
    }

     if (newCell) {
      if(currentCell?.current){
        currentCell.current.style.border = 'solid 2px black'
        let input = document.getElementById("dynamicInput");
        if (input) {
          input.remove();
        }
      }
      currentCell.current = newCell
      newCell.style.border = '3px solid #1890ff'
      let input = document.createElement("input");
      input.type = "text";
      input.style.position = "absolute";
      input.style.left = "-9999px";
      input.id = "dynamicInput";
      newCell.appendChild(input);
      input.addEventListener("input", handleInput);
      input.focus();
    }
  }
 const handleInput = (e) => {
    if(!currentCell || !currentCell.current) return
    let childNodes = currentCell.current.childNodes
    let inputIndex: any = null, textAreaIndex: any = null
    childNodes.forEach((node, index) => {
      console.log(node, 'node')
      if(node.tagName === 'INPUT' && !node.id) inputIndex = index
      if(node.tagName === 'TEXTAREA') textAreaIndex = index
    })
    console.log(e, 'e')

     if(inputIndex !== null){
       childNodes[inputIndex].value = e.target.value
     }else if(textAreaIndex !== null){
       childNodes[textAreaIndex].value = e.target.value
     }
  }
  useEffect(()=>{
      // 鼠标点击事件,因为第一次选中单元格肯定是要点击
      document.addEventListener("click", (e: MouseEvent) => {
      const target = e.target as HTMLElement

      // console.log(target.tagName, 'target')
      // 这里要判断被点击的对象是不是你需要监听的表格的单元格
      const isActive = (target.tagName === 'TD' || target.tagName === 'TH') && ...

      if (isActive) {
        if(currentCell?.current){ // 将原本被选中的单元格样式改为正常样式
          currentCell.current.style.border = 'solid 2px black'
          let input = document.getElementById("dynamicInput");
          if (input) {
            input.remove();
          }
        }
        // 新的单元格存起来,并高亮显示
        currentCell.current = target
        target.style.border = '3px solid #1890ff'
        let input = document.createElement("input");
        input.type = "text";
        input.id = "dynamicInput";
        input.style.position = "absolute";
        input.style.left = "-9999px";
        target.appendChild(input);
        input.addEventListener("input", handleInput);
        input.focus();
      } else {
        // 如果被点击的不是需要监听的地方,则整个表格“失去焦点”
        if(currentCell?.current){
          currentCell.current.style.border = 'solid 2px black'
          currentCell.current = null
          let input = document.getElementById("dynamicInput");
          if (input) {
            input.remove();
          }
        }
      }
    })
    document.addEventListener('keydown', function(e) {
      // console.log(e, 'e')
      if (e.key === 'ArrowUp' || e.key === 'ArrowDown' || e.key === 'ArrowLeft' || e.key === 'ArrowRight') {
        handleArrowKeys(e);
      }
    });
  },[])

后面这种方法就改成了给当前选中的单元格插入一个用户看不到的自动获取焦点的input,然后监听这个文本框的input事件,并实时将这个文本框的内容更新到对应的文本框。才刚实现这个,没有经过大量操作的测试,不知道会不会有什么bug,目前没有什么大问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1223464.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

简单算法——回溯、贪心、动态规划

回溯法 回溯法深度优先剪枝&#xff0c;实质就是用递归代替for循环。 仍然是一种暴力遍历的手段&#xff0c;通常与递归配合使用&#xff0c;用于解决单纯for循环无法处理的问题&#xff0c;比如组合、切割、子集、排列等问题——比如求n个数里的长度为k的组合&#xff0c;需要…

docker 安装mongodb 实现 数据,日志,配置文件外挂

docker 安装mongodb 实现数据&#xff0c;日志&#xff0c;配置文件外挂 1 背景 最近开发了一个评论系统之前用mysql来存储数据&#xff0c;但是考虑到后期业务增大访问量也会增大&#xff0c;为了兼容这种高并发的场景&#xff0c;因此经过多方面的考虑&#xff0c;我们最终…

调试/抓包工具

一、Fiddler【推荐window使用】 介绍&#xff1a;个人认为是 Windows 平台最好用的抓包工具&#xff1b; 下载&#xff1a;Fiddler | Web Debugging Proxy and Troubleshooting Solutions 使用方式&#xff1a;这一篇文章写的很全&#xff0c;认真看完就够用了 Fiddler 抓包工…

FISCO BCOS 3.0【02】配置和使用系统自带的控制台

官方技术文档&#xff1a;https://fisco-bcos-doc.readthedocs.io/zh-cn/latest/index.html 我们在官方技术文档的基础上&#xff0c;进行&#xff0c;对文档中一些不清楚的地方进行修正 控制台提供了向FISCO BCOS节点部署合约、发起合约调用、查询链状态等功能。 第一步. 安…

Linux本地docker一键部署traefik+内网穿透工具实现远程访问Web UI管理界面

文章目录 前言1. Docker 部署 Trfɪk2. 本地访问traefik测试3. Linux 安装cpolar4. 配置Traefik公网访问地址5. 公网远程访问Traefik6. 固定Traefik公网地址 前言 Trfɪk 是一个云原生的新型的 HTTP 反向代理、负载均衡软件&#xff0c;能轻易的部署微服务。它支持多种后端 (D…

Git详解及 github使用

1.1 关于版本控制 开始之前先看一个没有版本控制的例子 1.1.1 本地版本控制 本地版本控制系统 许多人习惯用复制整个项目目录的方式来保存不同的版本&#xff0c;或许还会改名加上备份时间以示区别。这么做唯一的 好处就是简单&#xff0c;但是特别容易犯错。有时候会混淆所在…

市级奖项+1,持安获「创业北京」创业创新大赛优秀奖!

2274个创业项目参赛 历经五个多月的激烈角逐 第六届“创业北京”创业创新大赛 终于圆满落下帷幕 持安科技在北京市总决赛中再创佳绩&#xff01; 荣获制造业赛道优秀奖 本次大赛由北京市人力资源和社会保障局、北京市发展和改革委员会等11家单位联合主办&#xff0c;以“创…

C语言--从键盘输入10个数字放在数组中,并输出

用scanf读取数字的时候要注意&#xff0c;可以输入一个数字&#xff0c;按一下回车&#xff0c;输入一个数字&#xff0c;按一下回车&#xff0c;也可以一次性输入完10个数据。&#xff08;中间可以用空格隔开&#xff0c;系统会自动识别&#xff09; 输出一:每按下一个数字&am…

数据库mysql详细教学

1024 byte 构成 1 kb 1024 KB > 1MB 1024 MB > 1GB 1024 GB > 1TB 1024 TB > 1PB 内存的数据&#xff0c;断电后会丢失。外存的数据&#xff0c;断电后数据还在~ “持久化” 这样的次&#xff0c;意思就是把数据写到硬盘上。 mysql的第一组基本操作&#xff1a;数…

Linux 系统误将 chmod 权限改成 了 000,如何恢复?

Linux 系统误将 chmod 权限改成 了 000&#xff0c;如何恢复? busybox 是 Linux 标配&#xff0c;含有大多数主流 Linux 命令&#xff0c;你可以把它的存在当作救急备份。简单功能都可以调用 busybox 完成。这也就意味着很多原始命令出故障的情况下都可以用 busybox 暂时替代。…

【js作用域】JavaScript中作用域的是什么?:从编译时其承担什么角色和查询作用域中的变量的角度解析作用域

&#x1f601; 作者简介&#xff1a;一名大四的学生&#xff0c;致力学习前端开发技术 ⭐️个人主页&#xff1a;夜宵饽饽的主页 ❔ 系列专栏&#xff1a;JavaScript进阶指南 &#x1f450;学习格言&#xff1a;成功不是终点&#xff0c;失败也并非末日&#xff0c;最重要的是继…

架构师篇 DDD领域驱动设计篇

一 DDD领域驱动设计 1.1 领域驱动设计 领域驱动设计(英文&#xff1a;Domain-Driven Design&#xff0c;缩写DDD)是一种模型驱动设计的方法&#xff0c;领域驱动设计常以战略设计与战术设计来将整个领域展现的淋漓尽致&#xff0c;其作用范围既面向业务也面向技术。从战略角度…

PHP排序sort()、asort() 和 ksort() 的区别及用法

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;CSDN领军人物&#xff0c;全栈领域优质创作者✌&#xff0c;CSDN博客专家&#xff0c;阿里云社区专家博主&#xff0c;2023年6月CSDN上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师…

SOLIDWORKS Electrical工程属性配置与图框

导读 很多工程师都是直接使用现有的图框&#xff0c;但是现有图框会遇到一些问题&#xff0c;自己想显示的内容不知道怎么设置出来&#xff0c;或者是图纸显示的内容太繁杂&#xff0c;行列号不符合自己的习惯。这些问题都是关于图框模板的设计。 一、关于工程属性设计的问题…

【每日一题】数位和相等数对的最大和

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;哈希表 写在最后 Tag 【哈希表】【数组】【2023-11-18】 题目来源 2342. 数位和相等数对的最大和 题目解读 在数组中找出数位和相等数对的和的最大值。 解题思路 方法一&#xff1a;哈希表 维护一个不同的数位和表…

es 算法函数 有点不太懂了没有大神给指点一下

我先说一下我对算法分析的理解 算法函数&#xff0c;我们使用算法函数给指定的数据提高对应的值的方式 比如我现在要给一家酒店排名提高排名&#xff0c;我们可以利用算法函数&#xff0c;提高酒店排名&#xff0c;因为酒店的名称 相关算法的使用场景 在使用的时候出现了这…

《循环双向链表》(带哨兵位的头节点)

目录 ​编辑 前言&#xff1a; 关于双向循环带头链表: 模拟实现双向循环带头链表&#xff1a; 1.typedef数据类型 2.打印链表 3.初始化链表&#xff1a; 4.创建节点 5.尾插 6.头插 7.尾删 8.头删 9.寻找节点 10.在节点前插入 11.删除指定节点 单链表和双链表的区别…

Android 解决CameraView叠加2个以上滤镜拍照黑屏的BUG (二)

1. 前言 这段时间&#xff0c;在使用 natario1/CameraView 来实现带滤镜的预览、拍照、录像功能。 由于CameraView封装的比较到位&#xff0c;在项目前期&#xff0c;的确为我们节省了不少时间。 但随着项目持续深入&#xff0c;对于CameraView的使用进入深水区&#xff0c;逐…

C++项目案例圆和点的关系 (涉及知识点:头文件定义类,cpp文件实现类,类和作用域,linux编译运行c++项目)

一.项目描述 点与圆有三种关系&#xff1a; 点在圆外 点在圆上 点在圆内计算点到圆心的距离就能判断点在圆的哪个地方。二.项目结构 三.include文件 3.1 Circle类的声明 Circle.h // 防止头文件重复包含 #pragma once // #include<iostream> #include "Point.h&…

PS学习笔记——图层

文章目录 图层面板图层类型新建图层新建方式图层颜色 操作图层修改图层名称选中图层隐藏图层调整图层顺序复制图层 图层面板 按F7可打开/关闭图层面板 该面板就是图层面板了 对所有图层进行筛选的按钮&#xff0c;第一个搜索框可以选择按什么方式进行筛选&#xff0c;支持&am…