tabindex Overuse Breaking Flow Fix
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
tabindexvalues - Use
tabindex="-1"only for programmatic focus targets - Use
tabindex="0"only on non-semantic interactive elements (custom widgets) - Remove
tabindexwhen elements are no longer interactive - Prefer native HTML elements (button, a, input, select) which are focusable by default
Common Mistakes with tabindex overuse
- Forgetting that lazy evaluation defers computation until the value is forced, causing space leaks with unevaluated thunks
- Using
returnto exit a function early instead of wrapping a pure value in the monad - 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
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro