如何将焦点设置到 InputText 元素?
How to set the focus to an InputText element?
使用 example from the Microsoft docs,我尝试以编程方式将焦点设置到输入元素。
不幸的是,该示例使用标准 <input type="text">
,而我想将其用于 InputText
element。
Microsoft 示例使用了一个扩展方法,该方法采用 ElementReference
:
public static Task Focus(this ElementReference elementRef, IJSRuntime jsRuntime)
{
return jsRuntime.InvokeAsync<object>(
"exampleJsFunctions.focusElement",
elementRef);
}
使用 InputText
,我看不出有什么办法可以得到这样的 ElementReference
。
用 InputText
代替我自己的 Focus()
重载,编译但没有显示任何视觉结果。所以我一无所知
我的问题
如何以编程方式将焦点设置到 InputText
元素?
您可以将 id
参数添加到您的 InputText
并修改您的 Focus
方法和 JavaScript 代码。
public async Task Focus(string elementId)
{
await JSRuntime.InvokeVoidAsync("exampleJsFunctions.focusElement", elementId);
}
focusElement: function (id) {
const element = document.getElementById(id);
element.focus();
}
注意:这与其说是正确的解决方案,不如说是一种变通方法,但 Blazor 似乎并不直接支持它。
有几种方法可以通过 Blazor 本机方式将焦点设置在元素上。这是一个:
创建一个派生自 InputBase<string>
的 class,它是 InputText
的基础 class,具有与 InputText 相同的功能。简而言之,将 the code of InputText 复制到您新创建的 class,并添加必要的功能。这是新的 class:TextBox.cs
public class TextBox : InputBase<string>
{
private ElementReference InputRef;
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.OpenElement(0, "input");
builder.AddMultipleAttributes(1, AdditionalAttributes);
builder.AddAttribute(2, "class", CssClass);
builder.AddAttribute(3, "value", BindConverter.FormatValue(CurrentValue));
builder.AddAttribute(4, "onchange", EventCallback.Factory.CreateBinder<string>
(this, __value => CurrentValueAsString = __value, CurrentValueAsString));
builder.AddElementReferenceCapture(5, (value) => {
InputRef = value; } );
builder.CloseElement();
}
[Inject] IJSRuntime JSRuntime { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await JSRuntime.InvokeVoidAsync("exampleJsFunctions.focusElement", InputRef);
}
}
protected override bool TryParseValueFromString(string value, out string result, out string validationErrorMessage)
{
result = value;
validationErrorMessage = null;
return true;
}
}
}
将此脚本放在 _Host.cshtml 文件的底部,就在
下面
<script src="_framework/blazor.server.js"></script>
<script>
window.exampleJsFunctions =
{
focusElement: function (element) {
element.focus();
}
};
</script>
注意事项:
1. 定义一个 ElementReference 变量来保存对输入元素的引用。
2. 在 BuildRenderTree 方法中,我添加了代码来捕获对
输入元素
3. 从 OnAfterRenderAsync 方法调用 focusElement JavaScript 函数。
这只执行一次。请注意,我不能使用 OnInitializedAsync
只执行一次的方法,但 ElementReference 变量可能
包含空值。
4. 请注意,您不能 运行 任何没有 EditForm 的表单组件...
重要提示:当您的浏览器处于最小化状态时,按 Ctrl+F5 可能会干扰在文本元素中看到光标。
使用代码:
<EditForm Model="@employee" OnValidSubmit="@HandleValidSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<TextBox @bind-Value="@employee.Name"/>
<button type="submit">Submit</button>
</EditForm>
@code {
private Employee employee = new Employee();
private void HandleValidSubmit()
{
Console.WriteLine("OnValidSubmit");
}
public class Employee
{
public int ID { get; set; } = 1;
public string Name { get; set; } = "Nancy";
}
}
我可以通过直接在 Host.chtml 文件中输入 JavaScript 来完成此操作(您也可以像演练建议的那样添加 .js 文件):
<script type="text/javascript">
window.exampleJsFunctions = {
focusElement: function (element) {
element.focus();
}
}
</script>
接下来,在扩展文件中,我添加了扩展(注意:由于命名标准,我将 Focus
重命名为 FocusAsync
:
public static async Task FocusAsync(this ElementReference elementRef, IJSRuntime jsRuntime)
{
await jsRuntime.InvokeVoidAsync(
"exampleJsFunctions.focusElement", elementRef);
}
然后在剃须刀页面(或组件)中,注入 IJSRuntime
,添加一个 ElementReference
并将其绑定到您要聚焦的元素(注意:方法名称已更改为遵守命名标准):
@inject IJSRuntime JSRuntime
@using JsInteropClasses
<input @ref="username" />
<button @onclick="SetFocusAsync">Set focus on username</button>
@code {
private ElementReference username;
public async Task SetFocusAsync()
{
await username.FocusAsync(JSRuntime);
}
}
在.NET5中会简单很多:
<button @onclick="() => textInput.FocusAsync()">Set focus</button>
<input @ref="textInput"/>
@code {
ElementReference textInput;
}
注意:此功能是在 .NET5 Preview 8 中引入的,因此在最终版本发布之前可能会发生变化!
另外值得一提的是,在 .NET5 RC1 JavaScript isolation 中是通过 JS 模块 export/import 引入的。因此,如果您仍然需要使用 JS 互操作,请不要污染 window
对象。
更新: .NET 5 已发布,此功能未更改。
我也很帅Nuget package which can do some convenient JS tricks for you e.g.: focusing previously active element without having a @ref
to it. See docs here.
现在 .net 5 可能是多余的,但如果您不想硬编码元素的 ID 或派生您自己的输入 class,实现此目的的简单方法是添加一个 CSS class 到元素,然后使用 getElementsByClassName 找到并设置焦点。
创建包含助手的脚本:
var JSHelpers = JSHelpers || {};
JSHelpers.setFocusByCSSClass = function () {
var elems = document.getElementsByClassName("autofocus");
if (elems.length == 0)
return;
elems[0].focus();
};
创建一个共享的'AutofocusByCSS'组件来处理在正确的时间调用JS:
@inject IJSRuntime JSRuntime
@code {
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender) JSRuntime.InvokeVoidAsync("JSHelpers.setFocusByCSSClass");
}
}
然后在您的页面中:
<InputText @bind-Value="editmodel.someThing" class="form-control autofocus" />
<AutofocusByCSS />
适用于归结为标准 HTML 元素的任何内容,并且可以通过更改 class 名称输出更轻松地更改自动对焦目标。
与@enet 的回答类似,我继承自 InputBase,但不是在 .razor
文件中而是在 .cs
文件中:
public abstract class MyInputBase<T> : InputBase<T>
{
/// <summary>
/// If true, this element will be focused when the page loads
/// </summary>
[Parameter] public bool Autofocus { get; set; }
/// <summary>
/// Optional. Sets the id on the input element as well as the for attibute on the label element
/// </summary>
[Parameter] public string? ElementId { get; set; }
[Inject] IJSRuntime? Js { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
if (Autofocus) await Js.InvokeVoidAsync("cs.focusElementById", ElementId);
}
await base.OnAfterRenderAsync(firstRender);
}
protected override void OnInitialized()
{
if (string.IsNullOrWhiteSpace(ElementId)) ElementId = Guid.NewGuid().ToString();
base.OnInitialized();
}
}
还有我的.js
:
var cs = {
focusElementById: function (id) {
setTimeout(function () {
var el = document.getElementById(id);
if (el) el.focus();
}, 500);
}
}
几个笔记。第一,MyInputBase<T>
是抽象的,它将 TryParseValue
下推到我对 InputText、InputNumber 等的单独实现,它们继承了 MyInputBase<T>
而不是微软的 InputBase<T>
.
另外,请注意我在 javascript 中设置了一个 timeout
。至少对我来说,如果我不这样做,聚焦元素是行不通的。
对于 .NET 6:
在您的代码中,在@code 中或在代码后面创建一个 InputText 变量:
private InputText inputTextForFocus;
在 Blazor InputText
组件中,引用该变量:
<InputText @ref=inputTextForFocus>
然后,在您的代码中的适当位置,您可以访问 InputText
的底层 Element
并调用 FocusAsync()
。注意 Element
可以为空。
例如,将焦点设置为启动:
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
if (inputTextForFocus.Element != null)
await inputTextForFocus.Element.Value.FocusAsync();
}
使用 example from the Microsoft docs,我尝试以编程方式将焦点设置到输入元素。
不幸的是,该示例使用标准 <input type="text">
,而我想将其用于 InputText
element。
Microsoft 示例使用了一个扩展方法,该方法采用 ElementReference
:
public static Task Focus(this ElementReference elementRef, IJSRuntime jsRuntime)
{
return jsRuntime.InvokeAsync<object>(
"exampleJsFunctions.focusElement",
elementRef);
}
使用 InputText
,我看不出有什么办法可以得到这样的 ElementReference
。
用 InputText
代替我自己的 Focus()
重载,编译但没有显示任何视觉结果。所以我一无所知
我的问题
如何以编程方式将焦点设置到 InputText
元素?
您可以将 id
参数添加到您的 InputText
并修改您的 Focus
方法和 JavaScript 代码。
public async Task Focus(string elementId)
{
await JSRuntime.InvokeVoidAsync("exampleJsFunctions.focusElement", elementId);
}
focusElement: function (id) {
const element = document.getElementById(id);
element.focus();
}
注意:这与其说是正确的解决方案,不如说是一种变通方法,但 Blazor 似乎并不直接支持它。
有几种方法可以通过 Blazor 本机方式将焦点设置在元素上。这是一个:
创建一个派生自 InputBase<string>
的 class,它是 InputText
的基础 class,具有与 InputText 相同的功能。简而言之,将 the code of InputText 复制到您新创建的 class,并添加必要的功能。这是新的 class:TextBox.cs
public class TextBox : InputBase<string>
{
private ElementReference InputRef;
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.OpenElement(0, "input");
builder.AddMultipleAttributes(1, AdditionalAttributes);
builder.AddAttribute(2, "class", CssClass);
builder.AddAttribute(3, "value", BindConverter.FormatValue(CurrentValue));
builder.AddAttribute(4, "onchange", EventCallback.Factory.CreateBinder<string>
(this, __value => CurrentValueAsString = __value, CurrentValueAsString));
builder.AddElementReferenceCapture(5, (value) => {
InputRef = value; } );
builder.CloseElement();
}
[Inject] IJSRuntime JSRuntime { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await JSRuntime.InvokeVoidAsync("exampleJsFunctions.focusElement", InputRef);
}
}
protected override bool TryParseValueFromString(string value, out string result, out string validationErrorMessage)
{
result = value;
validationErrorMessage = null;
return true;
}
}
}
将此脚本放在 _Host.cshtml 文件的底部,就在
下面<script src="_framework/blazor.server.js"></script>
<script>
window.exampleJsFunctions =
{
focusElement: function (element) {
element.focus();
}
};
</script>
注意事项: 1. 定义一个 ElementReference 变量来保存对输入元素的引用。 2. 在 BuildRenderTree 方法中,我添加了代码来捕获对 输入元素 3. 从 OnAfterRenderAsync 方法调用 focusElement JavaScript 函数。 这只执行一次。请注意,我不能使用 OnInitializedAsync 只执行一次的方法,但 ElementReference 变量可能 包含空值。 4. 请注意,您不能 运行 任何没有 EditForm 的表单组件...
重要提示:当您的浏览器处于最小化状态时,按 Ctrl+F5 可能会干扰在文本元素中看到光标。
使用代码:
<EditForm Model="@employee" OnValidSubmit="@HandleValidSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<TextBox @bind-Value="@employee.Name"/>
<button type="submit">Submit</button>
</EditForm>
@code {
private Employee employee = new Employee();
private void HandleValidSubmit()
{
Console.WriteLine("OnValidSubmit");
}
public class Employee
{
public int ID { get; set; } = 1;
public string Name { get; set; } = "Nancy";
}
}
我可以通过直接在 Host.chtml 文件中输入 JavaScript 来完成此操作(您也可以像演练建议的那样添加 .js 文件):
<script type="text/javascript">
window.exampleJsFunctions = {
focusElement: function (element) {
element.focus();
}
}
</script>
接下来,在扩展文件中,我添加了扩展(注意:由于命名标准,我将 Focus
重命名为 FocusAsync
:
public static async Task FocusAsync(this ElementReference elementRef, IJSRuntime jsRuntime)
{
await jsRuntime.InvokeVoidAsync(
"exampleJsFunctions.focusElement", elementRef);
}
然后在剃须刀页面(或组件)中,注入 IJSRuntime
,添加一个 ElementReference
并将其绑定到您要聚焦的元素(注意:方法名称已更改为遵守命名标准):
@inject IJSRuntime JSRuntime
@using JsInteropClasses
<input @ref="username" />
<button @onclick="SetFocusAsync">Set focus on username</button>
@code {
private ElementReference username;
public async Task SetFocusAsync()
{
await username.FocusAsync(JSRuntime);
}
}
在.NET5中会简单很多:
<button @onclick="() => textInput.FocusAsync()">Set focus</button>
<input @ref="textInput"/>
@code {
ElementReference textInput;
}
注意:此功能是在 .NET5 Preview 8 中引入的,因此在最终版本发布之前可能会发生变化!
另外值得一提的是,在 .NET5 RC1 JavaScript isolation 中是通过 JS 模块 export/import 引入的。因此,如果您仍然需要使用 JS 互操作,请不要污染 window
对象。
更新: .NET 5 已发布,此功能未更改。
我也很帅Nuget package which can do some convenient JS tricks for you e.g.: focusing previously active element without having a @ref
to it. See docs here.
现在 .net 5 可能是多余的,但如果您不想硬编码元素的 ID 或派生您自己的输入 class,实现此目的的简单方法是添加一个 CSS class 到元素,然后使用 getElementsByClassName 找到并设置焦点。
创建包含助手的脚本:
var JSHelpers = JSHelpers || {};
JSHelpers.setFocusByCSSClass = function () {
var elems = document.getElementsByClassName("autofocus");
if (elems.length == 0)
return;
elems[0].focus();
};
创建一个共享的'AutofocusByCSS'组件来处理在正确的时间调用JS:
@inject IJSRuntime JSRuntime
@code {
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender) JSRuntime.InvokeVoidAsync("JSHelpers.setFocusByCSSClass");
}
}
然后在您的页面中:
<InputText @bind-Value="editmodel.someThing" class="form-control autofocus" />
<AutofocusByCSS />
适用于归结为标准 HTML 元素的任何内容,并且可以通过更改 class 名称输出更轻松地更改自动对焦目标。
与@enet 的回答类似,我继承自 InputBase,但不是在 .razor
文件中而是在 .cs
文件中:
public abstract class MyInputBase<T> : InputBase<T>
{
/// <summary>
/// If true, this element will be focused when the page loads
/// </summary>
[Parameter] public bool Autofocus { get; set; }
/// <summary>
/// Optional. Sets the id on the input element as well as the for attibute on the label element
/// </summary>
[Parameter] public string? ElementId { get; set; }
[Inject] IJSRuntime? Js { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
if (Autofocus) await Js.InvokeVoidAsync("cs.focusElementById", ElementId);
}
await base.OnAfterRenderAsync(firstRender);
}
protected override void OnInitialized()
{
if (string.IsNullOrWhiteSpace(ElementId)) ElementId = Guid.NewGuid().ToString();
base.OnInitialized();
}
}
还有我的.js
:
var cs = {
focusElementById: function (id) {
setTimeout(function () {
var el = document.getElementById(id);
if (el) el.focus();
}, 500);
}
}
几个笔记。第一,MyInputBase<T>
是抽象的,它将 TryParseValue
下推到我对 InputText、InputNumber 等的单独实现,它们继承了 MyInputBase<T>
而不是微软的 InputBase<T>
.
另外,请注意我在 javascript 中设置了一个 timeout
。至少对我来说,如果我不这样做,聚焦元素是行不通的。
对于 .NET 6:
在您的代码中,在@code 中或在代码后面创建一个 InputText 变量:
private InputText inputTextForFocus;
在 Blazor InputText
组件中,引用该变量:
<InputText @ref=inputTextForFocus>
然后,在您的代码中的适当位置,您可以访问 InputText
的底层 Element
并调用 FocusAsync()
。注意 Element
可以为空。
例如,将焦点设置为启动:
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
if (inputTextForFocus.Element != null)
await inputTextForFocus.Element.Value.FocusAsync();
}