Skip to content

Roving Tabindex — Single Tab Stop for Widget Groups

DodaTech Updated 2026-06-28 3 min read

In this tutorial, you will learn about Roving Tabindex. We cover key concepts, practical examples, and best practices to help you master this topic.

Roving tabindex is a pattern where only one element in a widget group has tabindex=0 while others have tabindex=-1, with arrow keys shifting focus and updating tabindex values.

In this tutorial, you'll learn to implement roving tabindex for Keyboard Navigation.

What You'll Learn

By the end of this lesson, you'll understand the roving tabindex pattern, how to implement it for tabs, menus, and radio groups, and how it compares to aria-activedescendant.

Why It Matters

Without roving tabindex, users must press Tab multiple times to navigate within a widget. Roving tabindex makes complex widgets efficient.

Real-World Use

Doda Browser uses roving tabindex in the tab panel, toolbar, and menu components.

Roving Tabindex Flow

flowchart LR
  A[Widget group] --> B[One item tabindex='0']
  A --> C[Other items tabindex='-1']
  D[User presses ArrowRight] --> E[Remove tabindex from current]
  E --> F[Set tabindex='0' on next item]
  F --> G[Focus next item]
  G --> H[User presses Tab]
  H --> I[Focus leaves widget at current item]

Implementing Roving Tabindex

<div role="tablist" aria-label="Scan options">
  <button role="tab" aria-selected="true" tabindex="0">Quick</button>
  <button role="tab" aria-selected="false" tabindex="-1">Full</button>
  <button role="tab" aria-selected="false" tabindex="-1">Custom</button>
</div>
function handleArrowKey(event, container) {
  const items = Array.from(container.querySelectorAll('[role="tab"]'));
  const currentIndex = items.findIndex(el => el === document.activeElement);

  let newIndex;
  if (event.key === 'ArrowRight') {
    newIndex = (currentIndex + 1) % items.length;
  } else if (event.key === 'ArrowLeft') {
    newIndex = (currentIndex - 1 + items.length) % items.length;
  } else {
    return;
  }

  event.preventDefault();
  items.forEach((item, i) => {
    item.setAttribute('tabindex', i === newIndex ? '0' : '-1');
  });
  items[newIndex].focus();
}

The Tab Test

Tab should enter the widget at the currently active item and leave the widget from the same item. This is the key behavior of roving tabindex.

Roving Tabindex vs aria-activedescendant

Pattern Focus Tab stop Best for
Roving tabindex Moves between items Single tab stop per item Widgets with few items
aria-activedescendant Stays on container Single tab stop on container Widgets with many items or text input

Common Mistakes

  • Not resetting tabindex when selection changes: Stale tabindex values create multiple tab stops.
  • Using roving tabindex for very large lists: aria-activedescendant is better for hundreds of items.
  • Not handling Home and End keys: Users expect to jump to start and end.
  • Forgetting to manage tabindex on dynamic items: Added or removed items need tabindex updates.
  • Using roving tabindex for single-select without managing aria-selected: Selection state must match tabindex.

Practice and Challenge

1. What is roving tabindex? A pattern where only one item in a group is in the tab order.

2. What keys typically navigate within a roving tabindex widget? Arrow keys.

3. Where does focus go when Tab is pressed on a roving tabindex widget? To the next focusable element outside the widget, starting from the currently active item.

4. When should you use aria-activedescendant instead? When the widget has many items or includes a text input.

5. Challenge: Implement a radio group with roving tabindex. Only the selected radio should be in the tab order.

FAQ

Does roving tabindex work with all roles?

It works with composite roles like tablist, radiogroup, menubar, toolbar, and listbox.

What if there is no active item?

One item should always be active and in the tab order.

How do I handle Home and End keys?

Home focuses the first item, End focuses the last item.

Can I combine roving tabindex with aria-selected?

Yes. tabindex and aria-selected are typically updated together.

Does roving tabindex work on touch devices?

Touch devices do not have Tab keys. Roving tabindex is primarily for keyboard users.

Mini Project

Build a toolbar with three buttons using roving tabindex. Arrow keys navigate between buttons, Tab enters and exits. Verify with keyboard testing.

What's Next

Continue to Arrow Key Navigation to learn about implementing arrow key interactions.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro