Reputation: 387
I would like to find a way to focus on the next field when I click enter in the input using React.js
@autobind
handleKeyPress(event){
if(event.key === 'Enter'){
this.refs.email.focus();
}
}
@autobind
handleKeyPressEmail(event){
if(event.key === 'Enter'){
this.refs.zip_code.focus();
}
}
<input
onKeyPress={this.handleKeyPress}
ref = 'name'
/>
<input
onKeyPress={this.handleKeyPressEmail}
ref = 'email'
/>
<input
ref = 'zip_code'
/>
This is the best way I have found so far, however I don't want to repeat myself by creating a function everytime I want that to happen. Is there a better and cleaner way to implement this?
Upvotes: 23
Views: 42949
Reputation: 115
Without <form>
and TypeScript version.
Skip disabled inputs.
const onKeyPress: React.KeyboardEventHandler<HTMLInputElement> = useCallback(
(e) => {
if (e.key === "Enter") {
const inputs = Array.from(
// Get table or tbody whatever that contains all inputs. The number of parentElements depends on the structure of your html
e.currentTarget?.parentElement?.parentElement?.parentElement?.querySelectorAll(
"input"
) ?? []
).filter((e) => !e.disabled)
const index = inputs.indexOf(e.currentTarget)
inputs[index + 1]?.focus()
e.preventDefault()
}
},
[]
)
return <input type="number" onKeyPress={onKeyPress} />
Upvotes: 1
Reputation: 1743
If <form>
is present:
function handleEnter(event) {
if (event.keyCode === 13) {
const form = event.target.form;
const index = Array.prototype.indexOf.call(form, event.target);
form.elements[index + 1].focus();
event.preventDefault();
}
}
...
<form>
<input onKeyDown={handleEnter} />
<input onKeyDown={handleEnter} />
<input />
</form>
Without <form>
:
function useFocusNext() {
const controls = useRef([]);
const handler = (event) => {
if (event.keyCode === 13) {
// Required if the controls can be reordered
controls.current = controls.current
.filter((control) => document.body.contains(control))
.sort((a, b) =>
a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING
? -1 : 1
);
const index = controls.current.indexOf(event.target);
const next = controls.current[index + 1];
next && next.focus();
// IE 9, 10
event.preventDefault();
}
};
return useCallback((element) => {
if (element && !controls.current.includes(element)) {
controls.current.push(element);
element.addEventListener('keydown', handler);
}
}, []);
};
...
const focusNextRef = useFocusNext();
<input ref={focusNextRef} />
<input ref={focusNextRef} />
<button ref={focusNextRef}>Submit</button>
Upvotes: 33
Reputation: 12437
You can use componentDidMount and auto bind refs through a for-in loop.
http://codepen.io/jzmmm/pen/PzZgRX?editors=0010
constructor() {
super();
this._handleKeyPress = this._handleKeyPress.bind(this);
}
// Loop through the ref's object, and bind each of them to onkeypress
componentDidMount() {
for (let x in this.refs) {
this.refs[x].onkeypress = (e) =>
this._handleKeyPress(e, this.refs[x]);
}
}
// This checks ENTER key (13), then checks if next node is an INPUT
// Then focuses next input box
_handleKeyPress(e, field) {
if (e.keyCode === 13) {
e.preventDefault(); // Prevent form submission if button present
let next = this.refs[field.name].nextSibling;
if (next && next.tagName === "INPUT") {
this.refs[field.name].nextSibling.focus();
}
}
}
render() {
return (
<form>
<input type="text" name="name" ref='name' />
<input type="text" name="email" ref='email' />
<input type="text" name="zip_code" ref='zip_code' />
</form>
);
}
Upvotes: 9
Reputation: 387
This is how I managed to make it simpler:
@autobind
handleKeyPress(value, event){
if(event.key === 'Enter'){
this.refs[event].focus();
}
}
<input
onKeyPress={(event) => this.handleKeyPress('email', event)}
ref = 'name'
/>
<input
onKeyPress={(event) => this.handleKeyPress('zip_code', event)}
ref = 'email'
/>
<input
ref = 'zip_code'
/>
Upvotes: 1