FocusScope feature request: focus the next row, skipping over tabbable columns
See original GitHub issue🙋 Feature Request
Given a grid of elements, I’d like to be able to configure up and down arrow keypress event handlers that skip between rows. For example:
[element a] | element b | element c
element d | element e | element f
If element a is currently focused, pressing the down arrow key should focus element d, skipping elements b and c.
🤔 Expected Behavior
An option in the FocusManager API that lets us configure this somehow.
😯 Current Behavior
As far as I can tell, no such functionality exists — a workaround for the above table currently looks like:
const numColumns = new Array(3).fill(0);
numColumns.forEach(() => focusManager.focusNext());
💁 Possible Solution
I think the easiest solution would be to add support for a new numeric skip option directly in the focusNext and focusPrevious API. I can contribute the change if this isn’t too controversial.
There might be something clever that can be done with NodeFilter and indices, but not that I’m aware of right now.
🔦 Context / 💻 Examples
This has come up in a few places, but here’s an example of a grid I’m working with, where I’d like to configure sensible arrow keybindings:
https://user-images.githubusercontent.com/4248263/173683815-dbe92593-c299-49bc-aa03-e3c3a6046c36.mov
Ideally, right and left arrow keys would navigate to the next column over (i.e. the default behavior for focusNext / focusPrevious), while I’d like for down and up arrow keys to jump to the next row. The aforementioned workaround would work here, but a native solution would be even better!
🧢 Your Company/Team
Atlassian/Trello
Issue Analytics
- State:
- Created a year ago
- Reactions:2
- Comments:6 (4 by maintainers)

Top Related StackOverflow Question
I chatted with the team. The behavior is not something we want to change because it’s been like this in browsers for a long time, and it’d be tricky to change. I suggest not relying on the number of times ‘accept’ is called and instead using the solution I originally proposed using the bounding box; this will be more resilient to implementation changes and screen size differences.
I looked into it today, it actually appears to be a bit of a problem/quirk with the browser implementation, previousNode will call filter twice when the node in question has child nodes You can see in this example https://codesandbox.io/s/elegant-vaughan-wqw2pb?file=/src/App.js that is not React Spectrum/Aria, all the elements that have a string in them have accept called twice. While the empty ones only call accept once.
This is present in every browser and in jest dom. jest-dom https://github.com/jsdom/jsdom/blob/master/lib/jsdom/living/traversal/TreeWalker-impl.js#L74 chromium https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/dom/tree_walker.cc;l=196
I’ll check with the team tomorrow to see if this is a behavior we’d like to change. My personal stance is that it is the same everywhere, we shouldn’t change it, but we’ll see how others feel.