Skip to content

Tab navigation #2

Open
Open

Description

@fabianonunes

How do properly handle tab navigation?
Currently, it's not possible to continue pressing TAB after the last virtual item receives focus.

Check here: https://jsfiddle.net/mx5dzdod/

Activity

  1. developit commented on Sep 8, 2016

    @developit
    Owner

    Hi @fabianonunes! If you set the overacanCount prop, it will render that number of off-canvas buffer rows before and after the visible rows. This also has the effect of enabling tab scrolling. That should make your list fully tab navigable.

    Let me know if that works for you!

  2. fabianonunes commented on Sep 9, 2016

    @fabianonunes
    Author

    Hi, Jason!
    I think it's not that simple, because when the DOM element is recycled for another item, the current focus is lost.

    Consider a list with 10 visible items and overscanCount={10}. When the 18th item is focused, the next TAB could motivate a scroll, recycling all nodes and the old focus is lost.

    Same example, but with overscanCount={30}
    https://jsfiddle.net/gnL63f5d/

  3. developit commented on Sep 9, 2016

    @developit
    Owner

    @fabianonunes Yeah :( I had posted that and then started fiddling with the JSFiddle and realized the recycling kills it. I did manage to get it working, but it requires a little bit of effort! First, I added a focus listener to each row and persisted the currently focussed row id (just the index in this case) in the demo component's state. When rendering a given row (now a <Row> component), a focussed boolean can then be passed in as a prop that tells the row if it is currently the focussed item. If it is, after rendering it invokes .focus() on itself.

    Second, in order to make shift+tab work, I added a componentDidUpdate() hook that just calls the DOM .scrollIntoViewIfNeeded() method on whichever row is focussed. This scrolls the off-canvas rendered "-1th" item into view where there browser does not (it is not outside the default bounds for the browser's own focus scrolling behavior).

    Here's the working JSFiddle with the above two modifications in place:
    https://jsfiddle.net/developit/83h1jb2u/5/

    Sorry this is so convoluted! I'd love to see something cleaner.

  4. fabianonunes commented on Sep 9, 2016

    @fabianonunes
    Author

    @developit This solution won't work if (i) the component's root isn't a focusable element, (ii) we have two focusable elements by <Row/>.

    We could save the document.activeElement reference before render and reinforce focus on componentDidUpdate. But in this case, we need keyed children (to avoid reuse).

    Check here: https://jsfiddle.net/Lgd86uaw/2/
    Diff: fabianonunes@aedbd1

  5. developit commented on Sep 9, 2016

    @developit
    Owner

    For the first one, I think just adding tabIndex="0" to each row would make them focussable.
    For keyed children though, that sortof defeats the optimization here. Your document.activeElement technique is definitely on the right track though....

  6. fabianonunes commented on Sep 9, 2016

    @fabianonunes
    Author

    Yes. Keyed elements has a impact on perfomance. It's not an alternative.

  7. developit commented on Sep 9, 2016

    @developit
    Owner

    @fabianonunes Also I just realized - if the user scrolls the list using the mouse wheel / flick, hitting tab again uses the shifted viewport :(

    I'm actually thinking overriding tab is the only way to go here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions