Reputation: 675
I would like to emulate a Tab event, only with a down arrow key, in a React component.
This seems pretty simple on a first glance, when it is a single input using ref callbacks. However, I will have three lists of search results li's with an a href in them.
Does any one have a better approach than the below?
P.S: And I know, why recreate the tab function? ... a clients request! :-(
A little sample code:
import React, { Component } from "react"
export default class TestList extends Component {
constructor(props){
super(props);
this.state = {
focusedItem: 0,
}
this.nextFocus = this.nextFocus.bind(this)
this.handleKey = this.handleKey.bind(this)
};
componentDidMount() {
const list = this.refs.list.children
if (list.length > 0) {
let item = list[0]
// console.log(item.id)
// console.log(item.tabIndex)
let current = item.tabIndex
this.setState({
focusedItem: current,
})
item.getDOMNode().focus();
}
}
upState() {
if (this.state.focusedItem === 0) {
this.setState({
focusedItem: 1,
});
}
else {
let current = this.state.focusedItem
let nextState = current+1
console.log(nextState)
this.setState({
focusedItem: nextState,
});
}
}
nextFocus() {
const list = this.refs.list.children
this.upState();
const item = list[this.state.focusedItem]
console.log(item)
if (this.state.focusedItem < list.length) {
item.getDOMNode().focus();
}
}
handleKey(event) {
switch(event.keyCode){
case 9: // Enter key
break;
case 13: // Enter key
break;
case 27: // Esc key
break;
case 32: // spacebar
break;
case 37: // left
break;
case 38: // up
break;
case 39: // right
this.nextFocus();
break;
case 40: // down
this.nextFocus();
// React.findDOMNode() ???
//onFocus DOMEventTarget relatedTarget
break;
default:
}
}
render() {
const navList = [
{
name:'please',
link:'/#',
}, {
name:'please 2',
link:'/#'
}, {
name:'please 3',
link:'/#'
}, {
name:'please 4',
link:'/#'
}
];
return (
<ul ref='list'>
{ navList.map((item, index) => {
return (
<li key={index}
ref={'lidx'+index}
id={'elid-'+index}
onClick={ this.focus }
tabIndex={index}
onKeyDown={this.handleKey} >
<b>{item.name}</b>
</li>
);
})}
</ul>
)
}
}
Upvotes: 0
Views: 229
Reputation: 26
I had a similar problem recently and used event.target
to traverse between nodes. Something like this worked for me:
handleKey (e) {
switch(e.keyCode) {
//...
case 38: // up
if (e.target.previousSibling) e.target.previousSibling.focus()
break
case 40: // down
if (e.target.nextSibling) e.target.nextSibling.focus()
break
//...
}
I have no idea if this is considered a bad practice or if it solves your problem to full extent but using this you don't need to keep the current focused item in the state and you don't need to use refs.
You can also use e.target.parentNode.nextSibling
to further access adjacent lists.
Upvotes: 1