Auto focus an input element in a Blazor form on page load
When a user open a web page that contains a form, they expect the first input to be focused so they can start typing. In an HTML page, you can use the autofocus
attribute. However, the browser only check this attribute the first time the page is loaded. This means, this is not applicable in a SPA application without pre-rendering.
The workaround is to use the focus()
function once the page is rendered to focus the desired element.
#Focus an <input> element
First, you need to add the following JavaScript function to your project before the Blazor script:
<script>
function BlazorFocusElement(element) {
if (element instanceof HTMLElement) {
element.focus();
}
}
</script>
<script src="_framework/blazor.webassembly.js"></script>
Then, you can create an extension method to invoke this function:
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using System.Threading.Tasks;
namespace AutoFocusBlazorApp
{
public static class JSInteropFocusExtensions
{
public static ValueTask FocusAsync(this IJSRuntime jsRuntime, ElementReference elementReference)
{
return jsRuntime.InvokeVoidAsync("BlazorFocusElement", elementReference);
}
}
}
Now, you can use this method in a Blazor component by creating a reference to an element using @ref
and using the Focus
method on it:
@page "/"
@inject IJSRuntime JSRuntime
<EditForm Model="editItem">
<input type="text" @ref="firstInput" />
<button type="submit">submit</button>
</EditForm>
@code {
private ElementReference firstInput;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
// Focus the element
await JSRuntime.FocusAsync(firstInput);
}
}
}
In the following video, you can see that the input is focused as soon as the page is rendered:
Note that the ElementReference.FocusAsync()
method will be available in the next version of Blazor, so you won't need to inject the JSRuntime nor to add the JS function: Add an API to focus on elements
#Focus an InputText component
The previous code works ok for standard HTML elements. However, if you use the <InputText>
component, you cannot directly focus the input element as you cannot get the reference to the <input>
element.
<EditForm Model="@exampleModel">
@* How to focus this component? *@
<InputText @bind-Value="exampleModel.Name" />
<button type="submit">Submit</button>
</EditForm>
A solution is to create a new component that inherits from the built-in one and add a Focus
method. Create a new file named FocusableInputText.razor
with the following content:
@inherits InputText
@inject IJSRuntime JSRuntime
<input @ref="inputElement"
@attributes="AdditionalAttributes"
class="@CssClass"
value="@CurrentValue"
@onchange="EventCallback.Factory.CreateBinder<string>(this, __value => CurrentValueAsString = __value, CurrentValueAsString)" />
@code {
private ElementReference inputElement;
public ValueTask FocusAsync()
{
return JSRuntime.FocusAsync(inputElement);
}
}
In the parent component, you can create a reference to the component and call its FocusAsync
method on the OnAfterRenderAsync
method:
@page "/"
@inject IJSRuntime JSRuntime
<FocusableInputText @ref="ComponentRef" @bind-Value="editItem.BProp" />
@code {
private FocusableInputText ComponentRef;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await ComponentRef.FocusAsync();
}
}
}
If you want this feature to be built-in, you can upvote this GitHub issue to increase its priority: InputText component should provide a FocusAsync() method
Do you have a question or a suggestion about this post? Contact me!