[英]How do I implement a function to navigate through my react table using arrow keys?
我有一个 15 x 15 的反应表,每个单元格都包含输入字段。 我想实现一个 function ,这样每次我按下箭头键时,它都会将焦点移到那个方向。 到目前为止,这是我的董事会。 任何帮助表示赞赏!
let rows = [];
for (var i = 0; i < 15; i++){
let rowID = `row${i}`
let cell = []
for (var idx = 0; idx < 15; idx++){
let cellID = `cell${i}-${idx}`
let row = parseInt(`${i}`)
let col = parseInt(`${idx}`)
cell.push(
<td key={cellID} id={cellID}>
<div className={"tile"}>
<input>
</input>
</div>
</td>)
}
rows.push(<tr key={i} id={rowID}>{cell}</tr>)
}
return (
<div className="board">
<table>
{rows}
</table>
</div>
);
}```
要添加键盘控件,您需要处理以下内容:
这是我添加用户控件的方法,如果您愿意的话。 我敢打赌,您会更喜欢实施自己的解决方案。 我确信可以清理这段代码,但这是第一次通过。
你可以在这里尝试演示
import React, { useCallback, useEffect, useRef, useState } from "react";
const SimpleTable = () => {
const [numRows, numCols] = [3, 3]; // No magic numbers
const [activeIndex, setActiveIndex] = useState(-1); // Track which cell to highlight
const [isNavigating, setIsNavigating] = useState(false); // Track navigation
const [isEditing, setIsEditing] = useState(false); // Track editing
const [values, setValues] = useState([]); // Track input values
const boardRef = useRef(); // For setting/ unsetting navigation
const inputRefs = useRef([]); // For setting / unsetting input focus
// Handle input changes to store the new value
const handleChange = (e) => {
const { value } = e;
const newValues = Array.from(values);
newValues[activeIndex] = value;
setValues(newValues);
};
// Handle mouse down inside or outside the board
const handleMouseDown = useCallback(
(e) => {
if (boardRef.current && boardRef.current.contains(e.target)) {
if (e.target.className === "cell-input") {
setIsNavigating(true);
setIsEditing(true);
}
} else {
setIsNavigating(false);
}
},
[boardRef, setIsNavigating]
);
// Handle key presses:
// arrows to navigate, escape to back out, enter to start / end editing
const handleKeyDown = useCallback(
(e) => {
if (isNavigating) {
const { key } = e;
switch (key) {
case "ArrowUp":
// Move up a row, subtract num cols from index
if (!isEditing && activeIndex >= numRows)
setActiveIndex(activeIndex - numCols);
break;
case "ArrowDown":
// Move down a row, add num cols to index
if (!isEditing && activeIndex < numRows * numCols - numCols)
setActiveIndex(activeIndex + numCols);
break;
case "ArrowRight":
// Move one col right, add one
if (!isEditing && activeIndex < numRows * numCols - 1)
setActiveIndex(activeIndex + 1);
break;
case "ArrowLeft":
// Move one col left, subtract one
if (!isEditing && activeIndex > 0) setActiveIndex(activeIndex - 1);
break;
case "Enter":
if (isEditing) setIsEditing(false);
else if (isNavigating) setIsEditing(true);
else if (!isEditing) setIsNavigating(true);
break;
case "Escape":
// Stop navigating
if (isEditing) setIsEditing(false);
else if (isNavigating) setIsNavigating(false);
break;
default:
break;
}
}
},
[activeIndex, isNavigating, isEditing, numRows, numCols]
);
// Add listeners on mount, remove on unmount
useEffect(() => {
window.addEventListener("mousedown", handleMouseDown);
window.addEventListener("keydown", handleKeyDown);
return () => {
window.removeEventListener("mousedown", handleMouseDown);
window.removeEventListener("keydown", handleKeyDown);
};
}, [handleMouseDown, handleKeyDown]);
// When the index changes, determine if we should focus or blur the current input
const onIndexChange = useCallback(() => {
if (activeIndex >= 0 && activeIndex < numRows * numCols) {
const inputRef = inputRefs.current[activeIndex];
if (inputRef) {
if (isEditing) {
inputRef.focus();
} else {
inputRef.blur();
}
}
}
}, [activeIndex, isEditing, inputRefs, numRows, numCols]);
useEffect(onIndexChange, [activeIndex, onIndexChange]);
// When isEditing changes focus or blur the current input
const onIsEditingChange = useCallback(() => {
const inputRef = inputRefs.current[activeIndex];
if (!inputRef) return;
if (isNavigating && isEditing) {
inputRef.focus();
} else if (!isEditing) {
inputRef.blur();
}
}, [inputRefs, isEditing, activeIndex, isNavigating]);
useEffect(onIsEditingChange, [isEditing, onIsEditingChange]);
// When isNavigating changes, set the index to 0 or -1 (if not navigating)
const onIsNavigatingChange = useCallback(() => {
if (!isNavigating) {
setActiveIndex(-1);
} else if (activeIndex < 0) {
setActiveIndex(0);
}
}, [isNavigating, setActiveIndex, activeIndex]);
useEffect(onIsNavigatingChange, [isNavigating, onIsNavigatingChange]);
// Your original code with minor changes
let rows = [];
for (var i = 0; i < numRows; i++) {
let rowID = `row${i}`;
let cell = [];
for (var idx = 0; idx < numCols; idx++) {
let cellID = `cell${i}-${idx}`;
const index = i * numCols + idx;
cell.push(
<td key={cellID} id={cellID}>
<div className={`tile ${activeIndex === index ? "active" : ""}`}>
<input
value={values[activeIndex]}
onChange={handleChange}
className="cell-input"
onFocus={() => setActiveIndex(index)}
ref={(el) => (inputRefs.current[index] = el)}
/>
</div>
</td>
);
}
rows.push(
<tr key={i} id={rowID}>
{cell}
</tr>
);
}
return (
<div className="board" ref={boardRef}>
<table>
<tbody>{rows}</tbody>
</table>
</div>
);
};
export default SimpleTable;
这里还有一个小 CSS,我用它来显示哪个单元格处于活动状态:
.tile.active {
border: 1px solid rgb(0, 225, 255);
}
.tile {
border: 1px solid black;
}
.cell-input {
border: none;
outline: none !important;
}
如果您对具体细节有疑问,请告诉我!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.