Reputation: 2262
I want to implement the TAB behavior, but with pressing ENTER.
Here is my attempt to solve it, but it doesn't work due to not using Refs
.
My idea is to state in each input, which element should be focused next, and the Enter keyup event handler setting that value as the new focused field.
I've seen examples using useHook
but I can't figure how to use it, without spamming a ton of useState
's for each input.
Here is a Sandbox of the code below.
import React, { useState, useEffect } from "react";
function Form(props) {
// Holds the ID of the focused element
const [focusedElementId, setFocusedElementId] = useState("input-one");
// The actual focusing, after each re-render
useEffect(() => {
document.getElementById(focusedElementId).focus();
}, []);
useEffect(() => {
console.log("STATE:", focusedElementId);
}, [focusedElementId]);
// When Enter is pressed, set the focused state to the next element ID provided in each input
function handleKeyUp(e) {
e.which = e.which || e.keyCode;
if (e.which == 13) {
let nextElementId = e.target.attributes["data-focus"].value;
console.log(`HANDLER: Enter - focusing ${nextElementId}`);
setFocusedElementId(nextElementId);
}
}
return (
<div>
<div className="form-items" style={{ display: "flex" }}>
<div className="input-group">
<label>{"Input One"}</label>
<br />
<input
type={"text"}
id={"input-one"}
onKeyUp={handleKeyUp}
data-focus={"input-two"}
/>
</div>
<div className="input-group">
<label>{"Input Two"}</label>
<br />
<input
type={"text"}
id={"input-two"}
onKeyUp={handleKeyUp}
data-focus={"input-three"}
/>
</div>
<div className="input-group">
<label>{"Input Three"}</label>
<br />
<input
type={"text"}
id={"input-three"}
onKeyUp={handleKeyUp}
data-focus={"input-one"}
/>
</div>
</div>
</div>
);
}
export default Form;
Upvotes: 5
Views: 7973
Reputation: 851
This is similar to Tim's version, without needing refs, but his didn't work for me, but with some adaptations this works for me:
const handleKeyDown = (e) => {
if (e.key === 'Enter') {
const fields = Array.from(document.querySelectorAll('input')) || []
const position = fields.indexOf(e.target)
fields[position + 1] && fields[position + 1].focus()
}
}
And on the input, simply use onKeyDown={handleKeyDown}
Upvotes: 2
Reputation: 37
created this hook
import { useCallback, useEffect } from 'react'
export default function useFormTab() {
const keyDownHandler = useCallback((event: KeyboardEvent) => {
const target = event.target as HTMLButtonElement
if (event.keyCode === 13 && target.nodeName === "INPUT") {
var form = target.form;
var index = Array.prototype.indexOf.call(form, event.target);
// @ts-ignore
form.elements[index + 2].focus();
event.preventDefault();
}
}, []);
useEffect(() => {
document.addEventListener("keydown", keyDownHandler);
return () => document.removeEventListener("keydown", keyDownHandler);
}, [])
}
Upvotes: 0
Reputation: 115
Here's a much more scalable version; just attach at the container element:
function onKeyDown(e) {
if (e.key === 'Enter') {
const fields =
Array.from(e.currentTarget.querySelectorAll('input')) ||
[]
const position = fields.indexOf(
e.target // as HTMLInputElement (for TypeScript)
)
fields[position + 1] && fields[position + 1].focus()
}
}
Upvotes: 1
Reputation: 2262
I managed to get it working with refs
.
It looks simple and clean enough, but I am still curious about your opinions.
import React from "react";
function Form() {
let one = React.createRef();
let two = React.createRef();
let three = React.createRef();
// When Enter is pressed, set the focused state to the next element ID provided in each input
function handleKeyUp(e) {
e.which = e.which || e.keyCode;
// If the key press is Enter
if (e.which == 13) {
switch (e.target.id) {
case "input-one":
two.current.focus();
break;
case "input-two":
three.current.focus();
break;
case "input-three":
one.current.focus();
break;
default:
break;
}
}
}
return (
<div>
<div className="form-items" style={{ display: "flex" }}>
<div className="input-group">
<label>{"Input One"}</label>
<br />
<input
type={"text"}
id={"input-one"}
onKeyUp={handleKeyUp}
ref={one}
/>
</div>
<div className="input-group">
<label>{"Input Two"}</label>
<br />
<input
type={"text"}
id={"input-two"}
onKeyUp={handleKeyUp}
ref={two}
/>
</div>
<div className="input-group">
<label>{"Input Three"}</label>
<br />
<input
type={"text"}
id={"input-three"}
onKeyUp={handleKeyUp}
ref={three}
/>
</div>
</div>
</div>
);
}
export default Form;
Upvotes: 5