Focus management

Управление фокусом — критически важный аспект при создании доступных и удобных интерфейсов. В библиотеке MUI (Material-UI) предусмотрены встроенные инструменты для контроля фокуса, обработки событий фокусировки и создания логики переключения между интерактивными элементами.

Основы фокуса

В HTML-фокус определяется элементом, который в данный момент активен и готов к вводу с клавиатуры. В MUI большинство компонентов, таких как Button, TextField, Select, автоматически обрабатывают фокус благодаря поддержке стандартного HTML и React ref. Однако для сложных интерфейсов часто требуется ручное управление фокусом.

Пример использования ref для установки фокуса на компонент TextField:

import React, { useRef, useEffect } from 'react';
import TextField from '@mui/material/TextField';

function FocusExample() {
  const inputRef = useRef(null);

  useEffect(() => {
    inputRef.current.focus();
  }, []);

  return <TextField label="Имя" inputRef={inputRef} />;
}

Ключевой момент: MUI-компоненты поддерживают передачу ref через проп inputRef, что позволяет напрямую управлять DOM-элементом.

Автоматический фокус с autoFocus

MUI предоставляет проп autoFocus, который автоматически устанавливает фокус при монтировании компонента:

<TextField label="Email" autoFocus />

Использование autoFocus упрощает сценарии, где нужно выделять первый элемент формы при загрузке страницы. При работе с модальными окнами этот метод может конфликтовать с анимацией появления — важно учитывать время монтирования компонента.

Управление фокусом в модальных компонентах

MUI-модальные компоненты (Dialog, Popover, Menu) имеют встроенное управление фокусом для соответствия стандартам доступности WAI-ARIA.

  • Dialog автоматически переносит фокус на первый интерактивный элемент.
  • Фокус возвращается на исходный элемент после закрытия диалога.
  • Обрабатываются события Tab и Shift+Tab для циклического перемещения между интерактивными элементами внутри модального окна.

Пример использования Dialog с управлением фокусом:

import React, { useState } from 'react';
import Button from '@mui/material/Button';
import Dialog from '@mui/material/Dialog';
import DialogContent from '@mui/material/DialogContent';
import TextField from '@mui/material/TextField';

function DialogFocusExample() {
  const [open, setOpen] = useState(false);

  return (
    <>
      <Button onCl ick={() => setOpen(true)}>Открыть диалог</Button>
      <Dialog open={open} onCl ose={() => setOpen(false)}>
        <DialogContent>
          <TextField label="Имя" autoFocus />
          <TextField label="Email" />
        </DialogContent>
      </Dialog>
    </>
  );
}

Использование FocusTrap

Для нестандартных контейнеров и сложных интерфейсов можно применять компонент FocusTrap из @mui/base. Он позволяет ограничить фокус только элементами внутри контейнера.

import * as React from 'react';
import FocusTrap from '@mui/base/FocusTrap';
import Button from '@mui/material/Button';

function TrapExample() {
  return (
    <FocusTrap open={true}>
      <div style={{ border: '1px solid gray', padding: '20px' }}>
        <Button>Кнопка 1</Button>
        <Button>Кнопка 2</Button>
      </div>
    </FocusTrap>
  );
}

Основные преимущества использования FocusTrap:

  • Фокус не покидает контейнер при нажатии Tab.
  • Поддержка комбинации Shift+Tab для обратной навигации.
  • Совместимость с динамически добавляемыми элементами.

Переключение фокуса программно

Иногда требуется управлять фокусом в ответ на действия пользователя (например, после валидации формы). MUI позволяет использовать комбинацию ref и методов DOM focus().

Пример: перенос фокуса на поле с ошибкой:

function FormExample() {
  const emailRef = useRef(null);
  const passwordRef = useRef(null);

  const handleSubmit = () => {
    if (!emailRef.current.value) {
      emailRef.current.focus();
    } else if (!passwordRef.current.value) {
      passwordRef.current.focus();
    }
  };

  return (
    <>
      <TextField label="Email" inputRef={emailRef} />
      <TextField label="Пароль" inputRef={passwordRef} />
      <Button onCl ick={handleSubmit}>Войти</Button>
    </>
  );
}

События фокуса

MUI-компоненты поддерживают стандартные события:

  • onFocus — вызывается при получении фокуса.
  • onBlur — вызывается при потере фокуса.
  • onFocusVisible — специальное событие для визуализации фокуса с клавиатуры (поддержка Material Design).

Пример с использованием onFocusVisible:

import Button from '@mui/material/Button';
import { useState } from 'react';

function FocusVisibleExample() {
  const [focused, setFocused] = useState(false);

  return (
    <Button
      onFocusVisi ble={() => setFocused(true)}
      onB lur={() => setFocused(false)}
      variant={focused ? 'contained' : 'outlined'}
    >
      Кнопка
    </Button>
  );
}

Адаптивный фокус для доступности

MUI ориентируется на принципы WCAG, обеспечивая:

  • Видимую индикацию фокуса (:focus-visible).
  • Поддержку клавиатурной навигации без мыши.
  • Возможность кастомизации стилей фокуса через sx или styled.

Пример кастомного стиля фокуса:

<TextField
  label="Username"
  sx={{
    '& .MuiOutlinedInput-root.Mui-focused': {
      borderColor: 'green',
      boxShadow: '0 0 0 2px rgba(0,255,0,0.2)',
    },
  }}
/>

Итоговые рекомендации по фокусу в MUI

  • Использовать autoFocus для простых сценариев.
  • Применять ref и метод focus() для программного управления.
  • Для модальных окон доверять встроенной логике MUI.
  • Для нестандартных контейнеров использовать FocusTrap.
  • Обрабатывать события onFocusVisible для улучшения UX при клавиатурной навигации.
  • Всегда проверять доступность интерфейса с клавиатурой и скринридерами.

Эти подходы обеспечивают корректное поведение фокуса, повышают доступность и соответствуют современным стандартам Material Design.