Marius Schulz
Marius Schulz
Front End 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 being tabbed through in source order, the order in which the elements are defined within the HMTL 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 consecutive input field. Otherwise, users will very likely perceive the tab order as implausible if 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 is shown below:

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

<!-- ... -->

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

<!-- ... -->

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

<!-- ... -->

This approach has a small downside, though, because it's a little cumbersome to maintain input fields enumerated this way. If you ever want to change the order of the form elements, you will have to manually re-enumerate 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 litte 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 a simple increment.