Form Autofocusing

Controlling form focusing is always a "it depends" kind of situation. However, we can often times make intelligent decisions rather than leave the user hanging.

Imagine a common scenario. I have a pastry shop app and I click "New Donut". A form pops up with inputs for attributes such as the Nickname, Toppings, and Dough type for this donut recipe. I begin typing... oops! I have to tab through every interactable element on the page until I find this newly appeared form. Eep!

In such a scenario it makes sense to write reusable components for our form elements. Consider the following:

<button>New Donut</button>

{{#if this.creatingNewDonut}}
  <UiForm
    @shouldAutofocus={{true}}
    @submit={{this.submitHandler}}>
    <UiLabel>
      Nickname
      <UiInput />
    </UiLabel>
    <UiLabel>
      Toppings
      <UiInput />
    </UiLabel>
    <UiLabel>
      Dough Type
      <UiInput />
    </UiLabel>
  </UiForm>
{{/if}}

The above code example is written in htmlbars (handlebars for Ember) but it's a generalized example that could apply to any framework.

Now in our UiForm component we can write some code into your framework's render hook like so:

let focusableQuerySuffix = ':not([readonly]):not([disabled]):not([tabindex="-1"])';
let focusableQueries = [
  `input${focusableQuerySuffix}`,
  `textarea${focusableQuerySuffix}`,
  `select${focusableQuerySuffix}`,
];
let element = this.element.querySelector(focusableQueries.join(',')); // this.element will vary for your framework of choice, replace it with however you target your component's dom element
if (!element) return;

element.focus();

Focusing an element is the easy part, simply call .focus() on any dom element. Finding which dom element to query is the tricky part. Generally avoiding anything with a readonly, disabled or tabindex=-1 attribute is safe so long as you follow html semantics carefull in your application.

This could be expanded to support radio buttons, button elements and other interactable dom elements should you so choose.

Focus responsibly!