React: How to shift focus using arrow keys? (TreeView)
Asked Answered
N

2

8

I am looking for solution which allow to focus elements inside treeview using arrow keys.

Currently, I have treeView (ul) and treeNode (li). Each treeNode may have their own treeView and so on. Every treeNode has tabIndex="0" property to add possibility navigate trough the treeView using Tab key. It works fine. But I would like to add keyboard arrow support to do the same thing.

Any idea how to do this? P.S. I don't want to use any 3rd party libs expect pure React, JS.

<section>
  <header>
    { title }
  </header>
  <ul>
    <li>
      <section>
        <header>
          { title }
        </header>
        <ul>
          // etc.
        </ul>
      </section>
    </li>
  </ul>
<section>
Naphthol answered 28/6, 2017 at 14:38 Comment(0)
N
5

I have found a solution to move focus within treeView. First of all you should find all your nodes inside your tree. Then you can find focused element using document.activeElement. After that, you be able to find this item within your array nodes. (document.activeElement == nodes[i]) and remember index i. To move focus using arrow keys, just add eventListener to your node and handle it.

For example, to move upward you can do something like this:

if(arrowUp) { elements[i + 1].focus() }

Naphthol answered 30/6, 2017 at 9:32 Comment(0)
A
1

Roman's answer is helpful. I took this a step further by creating functions that handle moving focus up or down.

In the component I have my handleKeyDown function called on the onKeyDown event:

<UnorderedList id='unordered-list' onKeyDown={handleKeyDown} />

To call the functions I used an if statement like this below in my onKeyDown handler:

    const handleKeyDown = (e) => {
    if(e.key === 'ArrowDown') {
        moveFocusDown()
    }
    if(e.key === 'ArrowUp') {
        moveFocusUp()
    }
}

Next for the moveFocusDown function, I did the following:

    const moveFocusDown = () => {
    const listItems = document.querySelector('#unordered-list').childNodes
    const activeItem = document.activeElement
    for(let i = 0; i < listItems.length; i++) {

        const listLength = listItems.length
       if(activeItem === listItems[i] && activeItem !== listItems[listLength - 1]) {
        listItems[i + 1].focus()
       }
    }
}

The conditional activeItem === listItems[i] && activeItem !== listItems[listLength - 1] checks that the activeItem and the current index of the Unordered List child nodes are the same, then checks that the last element in the node list isn't the active element. This is needed to prevent moving focus to an element that doesn't exist.

The moveFocus up function is a little simpler:

    const moveFocusUp = () => {
    const listItems = document.querySelector('#menu').childNodes
    const activeItem = document.activeElement
    for(let i = 0; i < listItems.length; i++) {
       if(activeItem === listItems[i] && activeItem !== listItems[0]) {
        listItems[i - 1].focus()
       }
    }
}
Abagael answered 18/1, 2023 at 17:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.