admin管理员组文章数量:1417053
I have a 15 by 15 table in react, each cell containing input fields. I'd like to implement a function such that everytime I press an arrow key, it move's the focus to that direction. This is my board so far. Any help is appreciated!
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>
);
}```
I have a 15 by 15 table in react, each cell containing input fields. I'd like to implement a function such that everytime I press an arrow key, it move's the focus to that direction. This is my board so far. Any help is appreciated!
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>
);
}```
Share
Improve this question
asked May 17, 2021 at 15:25
abraham osmondabraham osmond
231 silver badge4 bronze badges
1
- Super late but for future visitors, at Neo4j we are using the following library: github./konsalex/table-nav – konsalex Commented Jul 27, 2023 at 14:25
1 Answer
Reset to default 5To add keyboard controls, you need to handle the following:
- Knowing which cell is active (useState)
- Knowing if the user is editing, navigating, or neither (useState)
- Storing the input values (useState)
- Reference to the board (useRef)
- Reference to the input elements (useRef)
- Handling mousedown and keydown events (event listeners, useEffect)
- Handling the side effects of changes to navigating, editing, index, etc. (useEffect, useCallback)
- Visualization for the user that they are navigating vs editing (CSS)
Here is my way of adding user controls, if you care to see it. I bet you would enjoy implementing your own solution more. I'm sure this code can be cleaned up, but this was a first pass at it.
You can try the demo here
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;
Also here's the small CSS that I use for showing which cell is active:
.tile.active {
border: 1px solid rgb(0, 225, 255);
}
.tile {
border: 1px solid black;
}
.cell-input {
border: none;
outline: none !important;
}
If you have questions about specifics, let me know!
版权声明:本文标题:javascript - How do I implement a function to navigate through my react table using arrow keys? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1745261781a2650387.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论