Вопрос или проблема
Я использую #each для отображения поля ввода для каждого члена массива tasks
. Когда я нажимаю кнопку “Добавить задачу”, новый элемент вставляется в массив, и в цикле #each появляется новое поле ввода.
Как мне сфокусироваться на добавленном поле ввода после нажатия кнопки “Добавить задачу”?
<script>
let tasks = [];
function addTask() {
tasks = [...tasks, { title: "" }];
}
</script>
{#each tasks as task}
<input type="text" bind:value={task.title} />
{/each}
<button on:click={addTask}>Добавить задачу</button>
Рич Харрис предлагает более красивое решение
Вы можете использовать use:action
:
Аctions — это функции, которые вызываются, когда элемент создается.
Например:
<script>
let tasks = [];
function addTask() {
tasks = [...tasks, { title: "" }];
}
function init(el){
el.focus()
}
</script>
{#each tasks as task}
<input type="text" bind:value={task.title} use:init />
{/each}
<button on:click={addTask}>Добавить задачу</button>
Вы можете использовать атрибут autofocus
:
<script>
let tasks = [];
function addTask() {
tasks = [...tasks, { title: "" }];
}
</script>
{#each tasks as task}
<input type="text" bind:value={task.title} autofocus />
{/each}
<button on:click={addTask}>Добавить задачу</button>
Обратите внимание, что вы получите предупреждение о доступности. Это связано с тем, что рекомендации по доступности фактически советуют не делать этого:
Люди с заболеваниями зрения могут испытывать дезориентацию, когда фокус перемещается без их разрешения. Кроме того, автоввод может вызвать проблемы у людей с нарушениями моторики, так как это может создать дополнительные трудности при переходе из области автоввода в другие места на странице.
Решение, использовать ли этот совет, остается за вами!
Вы можете использовать bind:this
и tick
Например:
<script>
import { tick } from 'svelte';
let tasks = [];
async function addTask() {
let newTask = { title: "" };
tasks = [...tasks, newTask];
await tick();
newTask.input.focus();
}
</script>
{#each tasks as task}
<input type="text" bind:value={task.title} bind:this={task.input} />
{/each}
<button on:click={addTask}>Добавить задачу</button>
Объяснение преимуществ моего подхода
Что произойдет, если массив tasks
изначально не пуст? В этом случае методы autofocus
и use:action
имеют недостаток в том, что при первоначальном отображении списка фокус находится на последнем поле. Это может быть нежелательно.
Мой подход контролирует фокус только в момент нажатия кнопки “Добавить”.
Ответ или решение
Чтобы сфокусироваться на вновь добавленных полях ввода в Svelte, можно воспользоваться несколькими подходами. Ниже приведены наиболее распространенные методы:
1. Использование директивы use:action
Директивы действий позволяют вам вызывать функции при создании элемента. В данном случае мы можем создать функцию init
, которая будет фокусироваться на элементе при его создании.
<script>
let tasks = [];
function addTask() {
tasks = [...tasks, { title: "" }];
}
function init(el) {
el.focus();
}
</script>
{#each tasks as task}
<input type="text" bind:value={task.title} use:init />
{/each}
<button on:click={addTask}>Добавить задачу</button>
2. Использование autofocus
Вы также можете использовать атрибут autofocus
, чтобы автоматически установить фокус на новом поле ввода. Однако этот подход может быть проблематичным с точки зрения доступности, так как внезапные изменения фокуса могут быть неудобны для пользователей с ограничениями возможностей.
<script>
let tasks = [];
function addTask() {
tasks = [...tasks, { title: "" }];
}
</script>
{#each tasks as task}
<input type="text" bind:value={task.title} autofocus />
{/each}
<button on:click={addTask}>Добавить задачу</button>
3. Использование bind:this
и tick
Этот метод позволяет получить доступ к элементу ввода и установить на него фокус только после добавления новой задачи. Он более контролируемый, и вы можете избежать проблем с фокусом для уже существующих элементов.
<script>
import { tick } from 'svelte';
let tasks = [];
async function addTask() {
let newTask = { title: "" };
tasks = [...tasks, newTask];
await tick();
newTask.input.focus();
}
</script>
{#each tasks as task}
<input type="text" bind:value={task.title} bind:this={task.input} />
{/each}
<button on:click={addTask}>Добавить задачу</button>
Преимущества последнего подхода
При использовании последнего метода, если массив tasks
изначально не пуст, вы не столкнетесь с проблемами, связанными с прежним фокусом. Метод autofocus
и use:action
могут оставить фокус на последнем введенном поле, что не всегда является желаемым поведением. Однако, используя tick
, вы можете явно контролировать, когда фокус должен перемещаться, что улучшает взаимодействие пользователя с интерфейсом.
Заключение
Каждый из перечисленных методов имеет свои преимущества и недостатки в зависимости от вашего конкретного случая использования. Рекомендуется выбирать тот подход, который наилучшим образом соответствует вашим требованиям по доступности и пользовательскому опыту.