How to Automatically Set Input Field Focus When Switching Tab Panels (Source Code Included)

Crafting the perfect online form is as much an art as is it engineering. There are so many subtle features we must consider. The ultimate goal is to make our user's task as easy and stress free as possible.

No one likes to fill out a long form. Seeing a long form creates instant anxiety. A common practice is to break long forms into small, digestible bites. Often this uses panels with either tabs or even a wizard style user experience.

This improves user success but creates new challenges for developers.

set focus in wizard and tab panels

Recently I was asked by a team how to set the focus to the initial input field as panels are toggled. This requires a level 5 magic spell!!!

Well not level 5, but it does need some simple JavaScript and an intermediate knowledge of CSS selectors.

From here I will share an example of a large form (well let's assume it is large), broken into multiple panels. I will then show how I automagically trigger the initial input field focus as panels toggle. In the end many of you will level up your front-end web development skills a notch or two. You can also apply this knowledge to other areas of your applications and streamline even more code!

All the source code is available on GitHub, in my giant demos and samples project. I have to warn you this is a scratch pad of me working through ideas so it is a bit chaotic. The demo page can be found within the JavaScript folder.

There are 2 files you can add to your websites and progressive web apps, tab-panel.js and tab-panel.css. They are located in the repo's /js and /css folders. You will see how I use the JavaScript module and CSS classes to create the user experience. You can extend them to fit your needs.

In this article, I won't focus on the construction of the tab and tab panels. We will explore toggling between panels and setting focus to the desired input field. I have another article I am working on to detail the tab panel setup. You will see how the JavaScript library handle's selecting a tab and associated workflow.

Creating Tab Panels with HTML and CSS

The first step is to create a set of tabs and the corresponding panels in HTML. Because this is a form, I like to wrap the FORM tags around the tab panel. The first row is the set of tabs or pills as they are sometimes called. These tabs serve as the navigation elements. For this example, I have three tabs and corresponding panels.

The next row wraps the panels. Inside of this row is the set of tab panels.

If you examine the tab elements you will see a custom attribute, tab-panel-id. The attribute is used as the visible panel is changed. It will set the automatically focused input field. You will see how that is done shortly.

<form>
        <div class="row col-12 my-2 px-0 tab-container">
        <div class="tab-pill-slider-container">
           <ul class="tab-pills">
                <li class="tab-pill mx-0 text-center">
                    <div class="tab-tab active" tab-panel-id="panel-1">
                    Tab 1
                      </div>
                </li>
                <li class="tab-pill mx-0 text-center">
                    <div class="tab-tab" tab-panel-id="panel-2">Tab 2</div>
                </li>
                <li class="tab-pill mx-0 text-center">
                    <div class="tab-tab" tab-panel-id="panel-3">Tab 3</div>
                </li>
            </ul>
        </div>
        <div class="tab-content clearfix col-12">
            <div class="tab-pane panel-1 flex-column active" id="panel-1">
             <!-- Form fields here -->       
        </div>
            <div class="tab-pane panel-2 flex-column" id="panel-2">
             <!-- Form fields here -->
            </div>
        <div class="tab-pane panel-3 flex-column" id="panel-3">
                  <!-- Form fields here -->
        </div>

        </div>
</form>

Inside each one of the panels are the actual form input fields. Normally, in a well designed form, the first input field will have the autofocus attribute set.

When a page is rendered the browser automatically looks for the first autofocus attribute. It then sets the focus there. If there are more than one designated the extras are ignored. If there are no input fields designated as autofocus, then nothing is set to an initial focus.

In this example the first input, exampleInputEmail1, has the autofocus attribute applied.

<div class="tab-pane panel-1 flex-column active" id="panel-1">
    <div class="form-group">
        <label for="exampleInputEmail1">Email address</label>
        <input type="email" class="form-control" id="exampleInputEmail1" autofocus
            aria-describedby="emailHelp" placeholder="Enter email" tabindex="100">
    </div>
    <div class="form-group">
        <label for="exampleInput1">Next Field</label>
        <input type="text" class="form-control" id="exampleInput1" placeholder="Some Value" tabindex="110">
    </div>
    <div class="form-check">
        <input type="checkbox" class="form-check-input" id="exampleCheck1" tabindex="120">
        <label class="form-check-label" for="exampleCheck1">Check me out</label>
    </div>
</div>

The second and third panels do not have a form field with the autofocus attribute. No need since the page already has one designated. Instead I have added a custom attribute, tab-focus="true". Setting the attribute value to true is not required, but I like to do this as a matter of completeness. The tab-panel module will look for this attribute when the panel is toggled to visible.

<div class="tab-pane panel-2 flex-column" id="panel-2">
    <div class="form-floating col-12">
        <textarea id="textarea_to_focus" class="form-control" name="email_markdown_body"
            type="text" tab-focus="true" tabindex="120" rows="5" cols="40" required
            placeholder="Focus On Me" aria-placeholder="Focus On Me" tabindex="200"></textarea>
        <label for="textarea_to_focus">Focus on the TextArea</label>
    </div>
    <div class="form-group">
        <label for="exampleInput3">Next Field</label>
        <input type="text" class="form-control" id="exampleInput3" placeholder="Some Value" tabindex="210">
    </div>
</div>
<div class="tab-pane panel-3 flex-column" id="panel-3">
    <div class="form-check">
        <input type="checkbox" class="form-check-input" id="exampleCheckToFocus"
            tab-focus="true" tabindex="300">
        <label class="form-check-label" for="exampleCheckToFocus">Focus On Me</label>
    </div>
    <div class="form-group">
        <label for="exampleInput4">Next Field</label>
        <input type="text" class="form-control" id="exampleInput4" placeholder="Some Value" tabindex="310">
    </div>
    <div class="form-group">
        <label for="exampleInput5">Next Field</label>
        <input type="text" class="form-control" id="exampleInput5" placeholder="Some Value" tabindex="320">
    </div>
</div>

For this example I chose three different types of input fields, input (type='email'), textarea and a checkbox. I wanted to convey this works with any input field, not a plain old text one.

Now to the dynamic actions with JavaScript!

Toggle or Switch Panels from Tab Buttons

Initiating the tab-panel module is easy, call the intitialize method. I will note you can pass an optional configuration object here. This can change some of the internal CSS selectors and a callback method for panel switching.

tab_panel.initialize();

Tab Panel 1 relies on the first form field having the autofocus attribute

If you provide a callback method it will be triggered as each tab is selected. A reference object is passed with the selected tab element, new active panel and the panel's id. This is completely optional, but can be helpful to extend the experience as you may need.

if (settings.toggleCallback){

    settings.toggleCallback({
        "tab": tab,
        "panelId": panelId,
        "panel": panel
    });
}

When a tab is selected, it triggers a quick workflow to toggle the visible panel. It then looks for an input field with either the autofocus or tab-focus attributes. If it fails to find a match, it will look for the first form field and select it. Unless you have turned the autoFocusCheck property off.


function setTabInitialFocus(panel) {

    if (settings.autofocus) {

        let initialFocus = panel.querySelector("[tab-focus], [autofocus]");

        if (initialFocus) {

            //if we have a matching element then set the focus
            initialFocus.focus();

        } else {

            //if there is no input with the tab-focus attribute, let's find form fields within the target panel
            initialFocus = panel.querySelector("input, textarea, select");

            if (initialFocus) {

               //you can only set the focus to a single input field, so chose the first in the list.
                    initialFocus.focus();

            }
        }
    }
}

A couple of notes here. The code calls querySelector from the active panel element, not the document. This is to limit the possible matches to just input fields within the visible tab panel. Next it calls querySelector, not querySelector all to limit the result to a single element, we only want to set focus to 1 field.

The CSS selector syntax being used it the attribute selector. This selector uses square brackes, [attribute_name='possible value'], to find a match. Note that I only used the attribute name, not the value for this effect. The value portion is optional, but can be very helpful when more than one option is possible.

The first step is to look for the first field with either the custom tab-focus attribute or autofocus. If that does not work it looks for common input fields.

Here you can see the second panel being selected and the textarea has initial focus.

tab panel 2 sets the textarea to initial focus

Next, the third panel sets the focus to the checkbox.

tab panel 3 sets the checkbox to initial focus

Summary

Subtle features like auto setting focus as a user progresses through your application may not be appreciated. But, they provide a sense of usability and comfort to the end user. This can go a long way to having a successful application.

The tab panel module automatically handles this action for you. As long as you do some simple configuration in your markup.

What are some other subtle improvements to online forms you think of? Could your current project benefit from small improvements like this, if so what are they? Leave your comment below.

Also feel free to use the tab-panel module to improve your applications.


Love2Dev
We specialize in Progressive Web Apps and Web Performance Optimization