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


    有类似于这样的表格,用的

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

    1. const currentCell = React.useRef<HTMLElement | null>() // 储存当前选中的单元格
    2. const handleArrowKeys = (event) => { // 当按下键盘方向键做的事情
    3. if (!currentCell || !currentCell.current) return;
    4. const cellIndex = currentCell?.current?.cellIndex;
    5. let newCell;
    6. switch (event.key) {
    7. case 'ArrowUp':
    8. newCell = currentCell.current?.parentElement?.previousElementSibling?.cells[cellIndex];
    9. break;
    10. case 'ArrowDown':
    11. newCell = currentCell.current?.parentElement?.nextElementSibling?.cells[cellIndex];
    12. break;
    13. case 'ArrowLeft':
    14. newCell = currentCell?.current?.previousElementSibling;
    15. break;
    16. case 'ArrowRight':
    17. newCell = currentCell?.current?.nextElementSibling;
    18. break;
    19. default:
    20. break;
    21. }
    22. if (newCell) {
    23. if(currentCell?.current){
    24. currentCell.current.style.border = 'solid 2px black'
    25. // currentCell.current.style.boxShadow = 'none'
    26. }
    27. currentCell.current = newCell
    28. newCell.style.border = '3px solid #1890ff'
    29. // newCell.style.borderColor = '#1890ff'
    30. // newCell.style.boxShadow = '0 0 10px 5px #1890ff'
    31. }
    32. }
    33. useEffect(()=>{
    34. // 鼠标点击事件,因为第一次选中单元格肯定是要点击
    35. document.addEventListener("click", (e: MouseEvent) => {
    36. const target = e.target as HTMLElement
    37. // console.log(target.tagName, 'target')
    38. // 这里要判断被点击的对象是不是你需要监听的表格的单元格
    39. const isActive = (target.tagName === 'TD' || target.tagName === 'TH') && ...
    40. if (isActive) {
    41. if(currentCell?.current){ // 将原本被选中的单元格样式改为正常样式
    42. currentCell.current.style.border = 'solid 2px black'
    43. }
    44. // 新的单元格存起来,并高亮显示
    45. currentCell.current = target
    46. target.style.border = '3px solid #1890ff'
    47. } else {
    48. // 如果被点击的不是需要监听的地方,则整个表格“失去焦点”
    49. if(currentCell?.current){
    50. currentCell.current.style.border = 'solid 2px black'
    51. currentCell.current = null
    52. }
    53. }
    54. })
    55. document.addEventListener('keydown', function(e) {
    56. // console.log(e, 'e')
    57. if (e.ctrlKey || e.altKey){
    58. // 这是按ctrl+ alt+的情况,很多快捷键方式不知道怎么模仿。
    59. } else if (e.key === 'ArrowUp' || e.key === 'ArrowDown' || e.key === 'ArrowLeft' || e.key === 'ArrowRight') {
    60. handleArrowKeys(e);
    61. } else if (
    62. e.key === 'Insert' || // 按下的是插入键
    63. e.key === 'Home' || // 按下的是Home键
    64. e.key === 'End' // 按下的是End
    65. // 其他需要处理的按键
    66. ) {
    67. return
    68. } else{
    69. if(!currentCell || !currentCell.current) return
    70. let childNodes = currentCell.current.childNodes
    71. let inputIndex: any = null, textAreaIndex: any = null
    72. childNodes.forEach((node, index) => {
    73. if(node.tagName === 'INPUT') inputIndex = index
    74. if(node.tagName === 'TEXTAREA') textAreaIndex = index
    75. })
    76. if(inputIndex !== null){
    77. if (
    78. e.key === 'Backspace' || // 按下的是退格键
    79. e.key === 'Delete'
    80. // 其他需要处理的按键
    81. ) {
    82. childNodes[inputIndex].value = ''
    83. } else if(e.key.length === 1) {
    84. childNodes[inputIndex].value = childNodes[inputIndex].value + e.key
    85. }
    86. }else if(textAreaIndex !== null){
    87. if (
    88. e.key === 'Backspace' || // 按下的是退格键
    89. e.key === 'Delete'
    90. // 其他需要处理的按键
    91. ) {
    92. childNodes[textAreaIndex].value = ''
    93. } else if(e.key.length === 1) {
    94. childNodes[textAreaIndex].value = childNodes[inputIndex].value + e.key
    95. }
    96. }
    97. }
    98. });
    99. },[])

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

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

    1. const currentCell = React.useRef<HTMLElement | null>() // 储存当前选中的单元格
    2. const handleArrowKeys = (event) => { // 当按下键盘方向键做的事情
    3. if (!currentCell || !currentCell.current) return;
    4. const cellIndex = currentCell?.current?.cellIndex;
    5. let newCell;
    6. switch (event.key) {
    7. case 'ArrowUp':
    8. newCell =
    9. currentCell.current?.parentElement?.previousElementSibling?.cells[cellIndex];
    10. break;
    11. case 'ArrowDown':
    12. newCell =
    13. currentCell.current?.parentElement?.nextElementSibling?.cells[cellIndex];
    14. break;
    15. case 'ArrowLeft':
    16. newCell = currentCell?.current?.previousElementSibling;
    17. break;
    18. case 'ArrowRight':
    19. newCell = currentCell?.current?.nextElementSibling;
    20. break;
    21. default:
    22. break;
    23. }
    24. if (newCell) {
    25. if(currentCell?.current){
    26. currentCell.current.style.border = 'solid 2px black'
    27. let input = document.getElementById("dynamicInput");
    28. if (input) {
    29. input.remove();
    30. }
    31. }
    32. currentCell.current = newCell
    33. newCell.style.border = '3px solid #1890ff'
    34. let input = document.createElement("input");
    35. input.type = "text";
    36. input.style.position = "absolute";
    37. input.style.left = "-9999px";
    38. input.id = "dynamicInput";
    39. newCell.appendChild(input);
    40. input.addEventListener("input", handleInput);
    41. input.focus();
    42. }
    43. }
    44. const handleInput = (e) => {
    45. if(!currentCell || !currentCell.current) return
    46. let childNodes = currentCell.current.childNodes
    47. let inputIndex: any = null, textAreaIndex: any = null
    48. childNodes.forEach((node, index) => {
    49. console.log(node, 'node')
    50. if(node.tagName === 'INPUT' && !node.id) inputIndex = index
    51. if(node.tagName === 'TEXTAREA') textAreaIndex = index
    52. })
    53. console.log(e, 'e')
    54. if(inputIndex !== null){
    55. childNodes[inputIndex].value = e.target.value
    56. }else if(textAreaIndex !== null){
    57. childNodes[textAreaIndex].value = e.target.value
    58. }
    59. }
    60. useEffect(()=>{
    61. // 鼠标点击事件,因为第一次选中单元格肯定是要点击
    62. document.addEventListener("click", (e: MouseEvent) => {
    63. const target = e.target as HTMLElement
    64. // console.log(target.tagName, 'target')
    65. // 这里要判断被点击的对象是不是你需要监听的表格的单元格
    66. const isActive = (target.tagName === 'TD' || target.tagName === 'TH') && ...
    67. if (isActive) {
    68. if(currentCell?.current){ // 将原本被选中的单元格样式改为正常样式
    69. currentCell.current.style.border = 'solid 2px black'
    70. let input = document.getElementById("dynamicInput");
    71. if (input) {
    72. input.remove();
    73. }
    74. }
    75. // 新的单元格存起来,并高亮显示
    76. currentCell.current = target
    77. target.style.border = '3px solid #1890ff'
    78. let input = document.createElement("input");
    79. input.type = "text";
    80. input.id = "dynamicInput";
    81. input.style.position = "absolute";
    82. input.style.left = "-9999px";
    83. target.appendChild(input);
    84. input.addEventListener("input", handleInput);
    85. input.focus();
    86. } else {
    87. // 如果被点击的不是需要监听的地方,则整个表格“失去焦点”
    88. if(currentCell?.current){
    89. currentCell.current.style.border = 'solid 2px black'
    90. currentCell.current = null
    91. let input = document.getElementById("dynamicInput");
    92. if (input) {
    93. input.remove();
    94. }
    95. }
    96. }
    97. })
    98. document.addEventListener('keydown', function(e) {
    99. // console.log(e, 'e')
    100. if (e.key === 'ArrowUp' || e.key === 'ArrowDown' || e.key === 'ArrowLeft' || e.key === 'ArrowRight') {
    101. handleArrowKeys(e);
    102. }
    103. });
    104. },[])

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

  • 相关阅读:
    vue3+ts打开echarts的正确方式
    揭开ChatGPT面纱(2):OpenAI主类源码概览
    景联文科技提供全方位图像标注服务
    基于A4988/DRV8825的四路步进电机驱动器
    剑指offer(C++)-JZ73:翻转单词序列(数据结构-队列 & 栈)
    [Qt]QListView 重绘实例之一:背景重绘
    聊聊HttpClient的close
    Linux安装MINIO
    【老生谈算法】matlabBOOST电路的设计与仿真——BOOST电路
    深入探讨Java面试中内存泄漏:如何识别、预防和解决
  • 原文地址:https://blog.csdn.net/ifmushroom/article/details/134454721