键盘方向键移动当前选中的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 = newCellnewCell.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 = targettarget.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) returnlet childNodes = currentCell.current.childNodeslet inputIndex: any = null, textAreaIndex: any = nullchildNodes.forEach((node, index) => {if(node.tagName === 'INPUT') inputIndex = indexif(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 = newCellnewCell.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) returnlet childNodes = currentCell.current.childNodeslet inputIndex: any = null, textAreaIndex: any = nullchildNodes.forEach((node, index) => {console.log(node, 'node')if(node.tagName === 'INPUT' && !node.id) inputIndex = indexif(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 = targettarget.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 = nulllet 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,目前没有什么大问题。
相关文章:
键盘方向键移动当前选中的table单元格,并可以输入内容
有类似于这样的表格,用的<table>标签。原本要在单元格的文本框里面输入内容,需要用鼠标一个一个去点以获取焦点,现在需要不用鼠标选中,直接用键盘的上下左右来移动当前正在输入的单元格文本框。 const currentCell React.u…...
(八)、基于 LangChain 实现大模型应用程序开发 | 基于知识库的个性化问答 (检索 Retrieval)
检索增强生成(RAG)的整体工作流程如下: 在构建检索增强生成 (RAG) 系统时,信息检索是核心环节。检索是指根据用户的问题去向量数据库中搜索与问题相关的文档内容,当我们访问和查询向量数据库时可能会运用到如下几种技术…...
高效案例检索工具,Alpha案例库智慧检索成为律师检索工具首选
“工欲善其事,必先利其器。”当今,律界同仁需要权衡的问题早已不是“要不要”使用法律科技,而是如何高质量、高效率地使用法律科技工具。在业内人士看来,随着人工智能技术的不断发展,法律行业科技化将成为不可逆转的趋…...
stable diffusion十七种controlnet详细使用方法总结
个人网站:https://tianfeng.space 前言 最近不知道发点什么,做个controlnet 使用方法总结好了,如果你们对所有controlnet用法,可能了解但是有点模糊,希望能对你们有用。 一、SD controlnet 我统一下其他参数&#…...
【机器学习基础】对数几率回归(logistic回归)
🚀个人主页:为梦而生~ 关注我一起学习吧! 💡专栏:机器学习 欢迎订阅!后面的内容会越来越有意思~ 💡往期推荐: 【机器学习基础】机器学习入门(1) 【机器学习基…...
团结引擎已全面支持 OpenHarmony 操作系统
Unity 中国宣布与开放原子开源基金会达成平台级战略合作。 据称团结引擎已全面支持 OpenHarmony 操作系统,同时将为 OpenHarmony 生态快速带来更多高品质游戏与实时 3D 内容。Unity 称现在用户可以 “在 OpenHarmony 框架中感受到与安卓和 iOS 同样丝滑的游戏体验”…...
【brpc学习案例实践一】rpc服务构造基本流程
前言 在crpc框架中,brpc简直越用越爽,平时工作中也常用到brpc,一直没来得及总结,抽空写点,也供自己查阅用。下附几个常用学习地址: brpc官网开源地址: https://github.com/luozesong/brpc/blob…...
Redis数据的持久化
Redis的持久化有两种方式: RDB(Redis Database)和AOF(Append Only File) 目录 一、RDB 保存方式 2、rdb在redis.conf文件中的配置 二、AOF 1、保存方式 2、aof方式持久化在redis.conf文件中的配置 三、持久化建…...
uniapp App 端 版本更新检测
function checkVersion() { var req { //升级检测数据 appid: plus.runtime.appid, version: plus.runtime.version }; const timestamp Date.parse(new Date()); config.server.query_news uni.reque…...
python用最小二乘法实现平面拟合
文章目录 数学原理代码实现测试 数学原理 平面方程可写为 A x B y C z D 0 AxByCzD0 AxByCzD0 假设 C C C不为0,则上式可以改写为 z a x b y d zaxbyd zaxbyd 则现有一组点 { p i } \{p_i\} {pi},则根据 x i , y i x_i,y_i xi,yi以及平面…...
SpringCloud微服务:Nacos和Eureka的区别
目录 配置: 区别: ephemeral设置为true时 ephemeral设置为false时(这里我使用的服务是order-service) 1. Nacos与eureka的共同点 都支持服务注册和服务拉取 都支持服务提供者心跳方式做健康检测 2. Nacos与Eu…...
基于Springboot+Vue的校园在线打印预约系统
基于SpringbootVue的校园在线打印预约系统的设计与实现 (1) 注册功能:允许学生、教职员工注册账户,并提供安全的身份验证机制,确保只有授权用户可以使用系统。 (2) 登录功能:店家或学生可以使用各自账号登录。登录后允许修改用户…...
计算机毕业设计选题推荐-掌心办公微信小程序/安卓APP-项目实战
✨作者主页:IT毕设梦工厂✨ 个人简介:曾从事计算机专业培训教学,擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…...
1.1二分查找
二分查找,主要是针对基本有序的数据来进行查找target。 二分法的思想很简单,因为整个数组是有序的,数组默认是递增的。 1.1 使用条件 用于查找的内容逻辑上来说是需要有序的查找的数量只能是一个,而不是多个 1.2 简介 首先选…...
提升工作效率,打造精细思维——OmniOutliner 5 Pro for Mac
在当今快节奏的工作环境中,如何高效地组织和管理我们的思维和任务成为了关键。而OmniOutliner 5 Pro for Mac正是为此而生的一款强大工具。无论你是专业写作者、项目经理还是学生,OmniOutliner 5 Pro for Mac都能帮助你提升工作效率,打造精细…...
idea显示pom.xml文件漂黄警告 Dependency maven:xxx:xxx is vulnerable
场景: idea警告某些maven依赖包有漏洞或者依赖传递有易受攻击包,如下: 解决: 1、打开idea设置,找到 File | Settings | Editor | Inspections 2、取消上述两项勾选即可...
Linux中安装部署环境(JAVA)
目录 在Linux中安装jdk 包管理器yum安装jdk JDK安装过程中的问题 验证安装jdk 在Linux中安装tomcat 安装mysql 在Linux中安装jdk jdk在Linux中的安装方式有很多种, 这里介绍最简单的方法, 也就是包管理器方法: 包管理器yum安装jdk Linux中常见的包管理器有: yumaptp…...
Zabbix Proxy分布式监控
目录 Zabbix Proxy简介 实验环境 proxy端配置 1.安装仓库 2.安装zabbix-proxy 3.创建初始数据库 4.导入初始架构和数据,系统将提示您输入新创建的密码 5.编辑配置文件 /etc/zabbix/zabbix_proxy.conf,配置完成后要重启。 agent客户端配置 zabbix…...
前端设计模式之【代理模式】
文章目录 前言介绍例子场景优缺点标题五后言 前言 hello world欢迎来到前端的新世界 😜当前文章系列专栏:前端设计模式 🐱👓博主在前端领域还有很多知识和技术需要掌握,正在不断努力填补技术短板。(如果出现错误&…...
Canal+Kafka实现MySQL与Redis数据同步(二)
CanalKafka实现MySQL与Redis数据同步(二) 创建MQ消费者进行同步 在application.yml配置文件加上kafka的配置信息: spring:kafka:# Kafka服务地址bootstrap-servers: 127.0.0.1:9092consumer:# 指定一个默认的组名group-id: consumer-group…...
centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...
家政维修平台实战20:权限设计
目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系,主要是分成几个表,用户表我们是记录用户的基础信息,包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题,不同的角色…...
Java面试专项一-准备篇
一、企业简历筛选规则 一般企业的简历筛选流程:首先由HR先筛选一部分简历后,在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如:Boss直聘(招聘方平台) 直接按照条件进行筛选 例如:…...
图表类系列各种样式PPT模版分享
图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...
【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论
路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中(图1): mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...
NPOI操作EXCEL文件 ——CAD C# 二次开发
缺点:dll.版本容易加载错误。CAD加载插件时,没有加载所有类库。插件运行过程中用到某个类库,会从CAD的安装目录找,找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库,就用插件程序加载进…...
【Post-process】【VBA】ETABS VBA FrameObj.GetNameList and write to EXCEL
ETABS API实战:导出框架元素数据到Excel 在结构工程师的日常工作中,经常需要从ETABS模型中提取框架元素信息进行后续分析。手动复制粘贴不仅耗时,还容易出错。今天我们来用简单的VBA代码实现自动化导出。 🎯 我们要实现什么? 一键点击,就能将ETABS中所有框架元素的基…...
人工智能 - 在Dify、Coze、n8n、FastGPT和RAGFlow之间做出技术选型
在Dify、Coze、n8n、FastGPT和RAGFlow之间做出技术选型。这些平台各有侧重,适用场景差异显著。下面我将从核心功能定位、典型应用场景、真实体验痛点、选型决策关键点进行拆解,并提供具体场景下的推荐方案。 一、核心功能定位速览 平台核心定位技术栈亮…...
土建施工员考试:建筑施工技术重点知识有哪些?
《管理实务》是土建施工员考试中侧重实操应用与管理能力的科目,核心考查施工组织、质量安全、进度成本等现场管理要点。以下是结合考试大纲与高频考点整理的重点内容,附学习方向和应试技巧: 一、施工组织与进度管理 核心目标: 规…...
