键盘方向键移动当前选中的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…...
101. 如何通过 Rancher Manager 收集指标
Environment 环境 Rancher 2.10 牧场主 2.10 Procedure 程序Rancher support might ask you to collect the Prometheus metrics for the cattle-cluster-agent. 牧场主支持可能会让你收集牛群集群代理的普罗米修斯指标。 They are available through the Rancher local clu…...
实战演练:基于快马平台与AI模型打造一个智能电商导购Agent
最近在尝试将AI技术落地到实际业务场景中,发现电商导购是个非常实用的切入点。今天就来分享下如何用InsCode(快马)平台快速搭建一个智能电商导购Agent的全过程。 项目架构设计 这个导购Agent采用前后端分离架构,主要分为三个模块: 前端交互…...
IT行业的项目经理考不考PMP证书?我劝你看完这篇在决定!
作为在 IT 圈摸爬滚打 8 年,从后端开发一路转型项目经理、带过 10 大小项目的老学长,最近总被身边技术小伙伴追问:想转 PM,必须考 PMP 吗?没证书就做不好项目管理吗?今天就用过来人的经验,跟大…...
内网渗透全流程拆解|从入门到实战,小白也能看懂的步骤
内网渗透不是“盲目尝试”,而是遵循固定流程的系统化操作,核心流程可概括为:信息收集→漏洞利用→权限提升→横向移动→权限维持→痕迹清理,每个环节环环相扣,缺一不可。本文将结合小白易理解的实战场景,详…...
fSpy完全上手指南:从基础到实战的零门槛教程
fSpy完全上手指南:从基础到实战的零门槛教程 【免费下载链接】fSpy A cross platform app for quick and easy still image camera matching 项目地址: https://gitcode.com/gh_mirrors/fs/fSpy 当你需要将一张普通的2D照片转换为精确的3D场景时,…...
给嵌入式开发者的英飞凌HSM实战指南:从AUTOSAR集成到密钥安全存储
英飞凌HSM深度实战:AUTOSAR集成与密钥管理全解析 在汽车电子领域,安全性能已经从"加分项"变成了"必选项"。想象一下,当一辆智能汽车以120公里时速行驶时,任何微小的安全漏洞都可能导致灾难性后果。这正是英飞…...
STM32除零运算不崩溃的机制与配置解析
1. STM32单片机除零运算不崩溃的底层机制解析 在嵌入式开发领域,STM32系列单片机因其出色的性能和丰富的外设资源而广受欢迎。许多从传统PC平台转向嵌入式开发的工程师都会发现一个有趣的现象:在STM32上执行除零操作时,程序竟然不会像在PC上那…...
告别原生IDE!用HBuilderX 3.6.8+和UTS插件5分钟搞定安卓Toast功能
5分钟解锁安卓Toast:HBuilderXUTS插件的高效开发实战 还在为Android Studio的臃肿和配置繁琐头疼?UniApp开发者现在有了更优雅的选择。想象一下:用熟悉的TypeScript语法直接调用原生API,无需切换开发环境,5分钟实现安卓…...
如何突破Office功能限制?本地化激活方案全解析
如何突破Office功能限制?本地化激活方案全解析 【免费下载链接】ohook An universal Office "activation" hook with main focus of enabling full functionality of subscription editions 项目地址: https://gitcode.com/gh_mirrors/oh/ohook 当…...
解锁3大智能功能:League-Toolkit让普通玩家也能玩转专业级游戏分析
解锁3大智能功能:League-Toolkit让普通玩家也能玩转专业级游戏分析 【免费下载链接】League-Toolkit An all-in-one toolkit for LeagueClient. Gathering power 🚀. 项目地址: https://gitcode.com/gh_mirrors/le/League-Toolkit 在英雄联盟的召…...
