edlee
edlee

Reputation: 675

Emulate onFocus and traverse Nodes with React JSX

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

Answers (1)

gdseck
gdseck

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

Related Questions