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

有类似于这样的表格,用的<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,目前没有什么大问题。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

简单漂亮的首页

效果图 说明 这个首页我也是构思了很久&#xff0c;才想出这个界面&#xff0c;大家喜欢的话&#xff0c;可以拿走去使用 技术的话&#xff0c;采用的就是vue的语法&#xff0c;但是不影响&#xff0c;很多样式我都是直接手敲出来的 代码实现 标语 <!-- 标语 start-->&…

《QT从基础到进阶·三十》QVariant的基础用法

很多时候&#xff0c;需要几种不同的数据类型需要传递&#xff0c;如果用结构体&#xff0c;又不大方便&#xff0c;容器保存的也只是一种数据类型&#xff0c;而QVariant则可以统统搞定。 QVariant可以保存QT和C常用类型&#xff0c;如果是自定义类型&#xff0c;比如struct,c…

【配置环境】VS Code怎么使用JavaScript的Mocha测试框架和Chai断言库

一&#xff0c;环境 Windows 11 家庭中文版&#xff0c;64 位操作系统, 基于 x64 的处理器VS Code 版本: 1.83.1 (user setup)Node.js 版本&#xff1a;20.9.0 二&#xff0c;安装背景 在运行测试用例时遇到 ReferenceError: describe is not defined 错误&#xff0c;网上搜寻…

JUC工具类_CyclicBarrier与CountDownLatch

最近被问到CyclicBarrier和CountDownLatch相关的面试题&#xff0c;CountDownLatch平时工作中经常用到&#xff0c;但是CyclicBarrier没有用过&#xff0c;一时答不上来&#xff0c;因此简单总结记录一下 1.什么是CyclicBarrier&#xff1f; 1.1 概念 CyclicBarrier&#xff…

MyBatis #{} 和 ${} 的区别

前言&#xff1a; #{} 和 ${} 的区别是 MyBatis 中一个常见的面试题&#xff0c;#{} 和 ${} 是MyBatis 中获取参数的两种方式&#xff0c;但我们在项目中大多数使用的都是 #{} 来获取参数&#xff0c;那么它们两个有什么区别呢&#xff1f; 区别 一. #{} 采用预编译 SQL&…

智能位移监测,更新传统井盖的功能

在城市的街道和人行道上&#xff0c;我们经常可以看到井盖的存在。井盖作为地下管道和设施的入口承载着重要的功能。然而过去我们可能忽视了一个重要的问题&#xff1a;井盖的位移可能会对人们产生潜在的威胁。为了保护我们的生活安全和交通畅通无阻和确保城市生命线安全稳定&a…

SQL Server如何建表

一、数据表的组成 实现完整性的约束有&#xff1a; –6个约束 –非空 not null –主键 primary key –唯一 unique –检查 check –默认 default –主键自增 identity 表约束 主键约束&#xff1a;值不能为null,且不能重复 非空约束&#xff1a;不能为null 默认约束&#xf…

004 OpenCV akaze特征点检测匹配

目录 一、环境 二、akaze特征点算法 2.1、基本原理 2.2、实现过程 2.3、实际应用 2.4、优点与不足 三、代码 3.1、数据准备 3.2、完整代码 一、环境 本文使用环境为&#xff1a; Windows10Python 3.9.17opencv-python 4.8.0.74 二、akaze特征点算法 特征点检测算法…

Es 拼音搜索无法高亮

目录 背景&#xff1a; Es 版本&#xff1a; 第一步 第二步 &#xff08;错误步骤 - 只是记录过程&#xff09; 第三步 第四步 第五步 第六步 第七步 背景&#xff1a; app 原有的搜索功能无法进行拼音搜索&#xff0c;产品希望可以支持&#xff0c;例如内容中含有&a…

rv1126-rv1109-openssh

这是一个工具&#xff0c;可以通过ssh远程登录来操作&#xff0c;非常逆天&#xff01; 于是rv1109代码自身自带有openssh 所以只需要打开config即可 diff --git a/buildroot/configs/rockchip_rv1126_rv1109_spi_nand_defconfig b/buildroot/configs/rockchip_rv1126_rv1109…

【力扣】从零开始的动态规划

【力扣】从零开始的动态规划 文章目录 【力扣】从零开始的动态规划开头139. 单词拆分解题思路 45. 跳跃游戏 II解题思路 5. 最长回文子串解题思路 1143. 最长公共子序列解题思路 931. 下降路径最小和解题思路 开头 本力扣题解用5题来引出动态规划的解题步骤&#xff0c;用于本…

我的 2023 秋招总结,拿到了大厂offer

2023秋招小结 前言 & 介绍 作为2024年毕业的学生&#xff0c;在2023年也就是今年秋招。 现在秋招快结束了&#xff0c;人生可能没有几次秋招的机会&#xff08;应该就一次&#xff0c;最多两次吧哈哈&#xff09;&#xff0c;也有一点感悟&#xff0c;所以小小总结一下。…

Unity中Shader纹理的过滤

文章目录 前言一、为什么要过滤&#xff1f;二、过滤方式1、Point(no filter) 无过滤2、Bilinear 双线性过滤3、Trilinear 三线性过滤 前言 Unity中Shader纹理的过滤 一、为什么要过滤&#xff1f; 事实上没有一个纹理上的纹素是与屏幕上的像素是一一对应的。 屏幕上的 一个…

【大模型应用开发教程】动手学大模型应用开发,一起探索LLM Universe

动手学大模型应用开发 01 开源初心02 教程内容03 学习指南04 文章最后 原文链接-奇想星球 LLM 正逐步成为信息世界的新革命力量&#xff0c;其通过强大的自然语言理解、自然语言生成能力&#xff0c;为开发者提供了新的、更强大的应用开发选择。随着国内外井喷式的 LLM API 服…

Flutter笔记:桌面应用 窗口定制库 bitsdojo_window

Flutter笔记 桌面应用窗口管理库 bitsdojo_window 作者&#xff1a;李俊才 &#xff08;jcLee95&#xff09;&#xff1a;https://blog.csdn.net/qq_28550263 邮箱 &#xff1a;291148484163.com 本文地址&#xff1a;https://blog.csdn.net/qq_28550263/article/details/13446…

开源与闭源:大模型时代的技术交融与商业平衡

一、开源和闭源的优劣势比较 1.1 开源 优势&#xff1a; 1.技术共享与吸引人才&#xff1a; 开源促进了技术共享&#xff0c;吸引了全球范围内的人才参与大模型的发展&#xff0c;形成了庞大的开发者社区。 2.推动创新&#xff1a; 开源模式鼓励开发者共同参与&#xff0c;推动…

LrC ACR :优化的 AI 天空蒙版

在 Lightroom Classic 和 Adobe Camera Raw 中创建基于 AI 技术的天空蒙版时&#xff0c;可能由于底层算法的原因&#xff0c;选中的天空蒙版在边缘处有晕开的现象&#xff08;又称为“出血” Bleed&#xff09;&#xff0c;从而导致天空蒙版不是很精准。 本文提供了一种特殊方…

vue监听对象属性值变化

一、官方文档 二、实现方法 方法一、直接根据watch来监听 export default {data() {return {object: {username: ,password: }}},watch: {object.username(newVal, oldVal) {console.log(newVal, oldVal)}} }方法二&#xff1a;利用watch和computed来实现监听 利用computed定…

腾讯云4核8G服务器配置价格表,轻量和CVM标准型S5实例

腾讯云4核8G服务器S5和轻量应用服务器优惠价格表&#xff0c;轻量应用服务器和CVM云服务器均有活动&#xff0c;云服务器CVM标准型S5实例4核8G配置价格15个月1437.3元&#xff0c;5年6490.44元&#xff0c;轻量应用服务器4核8G12M带宽一年446元、529元15个月&#xff0c;腾讯云…

【LeetCode刷题日志】20.有效的括号

&#x1f388;个人主页&#xff1a;库库的里昂 &#x1f390;C/C领域新星创作者 &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏✨收录专栏&#xff1a;LeetCode 刷题日志&#x1f91d;希望作者的文章能对你有所帮助&#xff0c;有不足的地方请在评论区留言指正&#xff0c;…