Skip to content

tabindex Overuse Breaking Flow Fix

DodaTech Updated 2026-06-24 3 min read

In this tutorial, you'll learn about tabindex overuse breaking flow fix. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

The Problem

Overusing tabindex attributes creates unpredictable focus orders, makes pages hard to navigate, and breaks the expected keyboard flow. Positive tabindex values, tabindex on non-interactive elements, and forgetting to remove tabindex after dynamic changes all cause problems.

Quick Fix

Step 1: Remove all positive tabindex values

<!-- Wrong — manual focus order is hard to maintain -->
<input tabindex="3" name="name">
<input tabindex="1" name="email">
<button tabindex="2">Submit</button>

<!-- Right — let DOM order determine focus -->
<input name="name">
<input name="email">
<button>Submit</button>

<!-- Tab order: name → email → Submit -->

Step 2: Do not add tabindex to non-interactive elements

<!-- Wrong — tabindex on paragraph -->
<p tabindex="0">Read this important text</p>

<!-- Wrong — tabindex on heading -->
<h2 tabindex="0">Section Title</h2>

<!-- Right — headings and paragraphs are not interactive -->
<!-- Screen readers navigate them via headings and reading mode -->
<h2>Section Title</h2>
<p>Read this important text</p>

Step 3: Remove tabindex after dynamic changes

// Wrong — tabindex remains after modal closes
function openModal() {
    const modal = document.getElementById('modal');
    modal.style.display = 'block';
    modal.querySelector('button').focus();
}

function closeModal() {
    document.getElementById('modal').style.display = 'none';
    // Background elements still have tabindex...
}

// Right — manage tabindex properly
function openModal() {
    const modal = document.getElementById('modal');
    modal.style.display = 'block';

    // Disable background interactivity
    document.querySelectorAll('#page-content a, #page-content button').forEach(el => {
        el.dataset.originalTabindex = el.tabIndex;
        el.tabIndex = -1;
    });

    modal.querySelector('button').focus();
}

function closeModal() {
    document.getElementById('modal').style.display = 'none';

    // Restore background interactivity
    document.querySelectorAll('#page-content a, #page-content button').forEach(el => {
        el.tabIndex = parseInt(el.dataset.originalTabindex) || 0;
    });
}

Step 4: Use tabindex="-1" only for programmatic focus

// Right — tabindex="-1" for programmatic focus only
<div id="section1" tabindex="-1">
    <h2>Section 1</h2>
</div>
<div id="section2" tabindex="-1">
    <h2>Section 2</h2>
</div>

<button onclick="document.getElementById('section1').focus()">
    Go to Section 1
</button>
<button onclick="document.getElementById('section2').focus()">
    Go to Section 2
</button>
// Focus can reach these via .focus(), but not via Tab key

Step 5: Avoid tabindex="0" on everything

<!-- Wrong — every element has tabindex="0" -->
<div tabindex="0" onclick="doSomething()">Action 1</div>
<div tabindex="0" onclick="doSomethingElse()">Action 2</div>

<!-- Right — use semantic HTML with natural focus -->
<button onclick="doSomething()">Action 1</button>
<button onclick="doSomethingElse()">Action 2</button>
<!-- Native buttons are focusable by default — no tabindex needed -->

Prevention

  • Never use positive tabindex values
  • Use tabindex="-1" only for programmatic focus targets
  • Use tabindex="0" only on non-semantic interactive elements (custom widgets)
  • Remove tabindex when elements are no longer interactive
  • Prefer native HTML elements (button, a, input, select) which are focusable by default

Common Mistakes with tabindex overuse

  1. Forgetting that lazy evaluation defers computation until the value is forced, causing space leaks with unevaluated thunks
  2. Using return to exit a function early instead of wrapping a pure value in the monad
  3. Mixing let bindings with <- bindings in do notation, producing type errors

These mistakes appear frequently in real-world A11Y code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.

Practice Exercise

Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.

This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.

FAQ

### What is the default tabindex value for interactive elements?

Native interactive elements (a, button, input, select, textarea) have an implicit tabindex="0" without needing the attribute. Do not add tabindex="0" to these elements — it is redundant.

Can tabindex="0" make a div focusable?

Yes. tabindex="0" makes any element focusable via Tab. But it does not add interactivity — you still need JavaScript for click/keyboard handlers and ARIA roles for screen readers. Always prefer native semantic elements first.

How do I test if my tabindex usage is correct?

Use the browser's Tab key to navigate the full page. The focus should follow a logical visual order. Use the Accessibility panel in Chrome DevTools > Inspection tab to see the computed tab order. Extensions like axe DevTools also flag tabindex issues.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro