Marius Schulz
Marius Schulz
Software Engineer

Better tabindex Handling with Razor

I've recently been working on an ASP.NET MVC project with plenty of views that are highly form-centric. That is, the main section of those pages is made up of a big form with lots of input fields.

When a user visits one of these pages, I want them to quickly be able to start entering form data. Because of that, I've added the tabindex="1" attribute to the first <input> tag. Now, when the user presses the TAB key, the first input field is focused right away.

However, there's a usability issue here. When the user presses the TAB key again while the first input field is still focused, the second input field does not receive focus. This is because every element without a tabindex attribute is tabbed through in source order, the order in which the elements are defined in the HTML of the page.

Here's what MDN says about tabindex:

An element with a 0 value, an invalid value, or no tabindex value should be placed after elements with a positive tabindex in the sequential keyboard navigation order.

Therefore, if the first input field has the tabindex attribute set, so should every subsequent input field. Otherwise, users will very likely find the tab order confusing when they suddenly see the cursor jump around (seemingly) randomly.

The easy fix for this issue would be to add the tabindex attribute to all input fields and manually increment its value, as shown below:

<input type="text" tabindex="1" />

<!-- ... -->

<input type="text" tabindex="2" />

<!-- ... -->

<input type="text" tabindex="3" />

<!-- ... -->

This approach has a small downside, though: it's a little cumbersome to maintain input fields numbered this way. If you ever want to change the order of the form elements, you will have to manually renumber the tab indices.

If the source order of the input fields matches their desired tab order, which should usually be the case, you can simplify your life a little bit by using a variable to keep track of the current index value:

@{
    int tabindex = 0;
}

<input type="text" tabindex="@(++tabindex)" />

<!-- ... -->

<input type="text" tabindex="@(++tabindex)" />

<!-- ... -->

<input type="text" tabindex="@(++tabindex)" />

<!-- ... -->

You can now freely reorder the input fields without having to worry about messing up the proper tab order. It's a simple trick, really, but if you apply it, there's one less thing to keep track of, even if it's just an increment.