When a user opens a web page containing a form, they expect the first input to be focused so they can start typing. In HTML, you can use the autofocus attribute. However, the browser only checks this attribute the first time the page loads. This makes it unsuitable for SPA applications without pre-rendering.
The workaround is to call the focus() function after the page renders, targeting the desired element.
First, you need to add the following JavaScript function to your project before the Blazor script:
HTML
<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:
C#
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 capturing a reference to an element with @ref and calling FocusAsync on it:
Razor
@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 or add the JS function: Add an API to focus on elements
#Focus an InputText component
The previous code works well for standard HTML elements. However, if you use the <InputText> component, you cannot directly focus the input because there is no way to obtain a reference to the underlying <input> element.
Razor
<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:
Razor
@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 capture a reference to the component and call its FocusAsync method from the OnAfterRenderAsync lifecycle method:
Razor
@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!