Клавиатурная навигация — важный аспект доступности интерфейсов. В контексте всплывающих уведомлений она обеспечивает управление без использования мыши: перемещение фокуса, закрытие уведомлений, взаимодействие с кнопками и ссылками внутри них.
Библиотека iziToast по умолчанию ориентирована на визуальное взаимодействие, однако поддержка клавиатуры может быть реализована за счёт правильной настройки параметров и добавления обработчиков событий.
При появлении уведомления важно определить, должен ли фокус автоматически перемещаться внутрь него. В стандартной конфигурации iziToast этого не делает, поэтому поведение следует реализовать вручную.
Пример установки фокуса на кнопку внутри уведомления:
iziToast.show({
title: 'Уведомление',
message: 'Доступно с клавиатуры',
buttons: [
['<button>ОК</button>', function (instance, toast, button) {
instance.hide({ transitionOut: 'fadeOut' }, toast);
}]
],
onOpened: function (instance, toast) {
const btn = toast.querySelector('button');
if (btn) btn.focus();
}
});
Ключевой момент — использование обработчика onOpened,
который срабатывает после добавления уведомления в DOM.
Для повышения удобства необходимо предусмотреть закрытие уведомлений
с помощью клавиш, например Escape.
Добавление глобального обработчика:
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape') {
document.querySelectorAll('.iziToast').forEach(toast => {
iziToast.hide({}, toast);
});
}
});
Особенности:
Если уведомление содержит несколько интерактивных элементов (кнопки, ссылки), важно обеспечить корректный порядок табуляции.
iziToast использует обычную DOM-структуру, поэтому порядок определяется:
tabindexПример:
buttons: [
['<button tabindex="0">Подтвердить</button>', handler],
['<button tabindex="0">Отмена</button>', handler]
]
Если необходимо исключить элемент из табуляции:
<button tabindex="-1">Скрытая кнопка</button>
В некоторых случаях требуется удерживать фокус внутри уведомления, пока оно активно. Это особенно важно для модальных уведомлений.
Пример простой реализации:
function trapFocus(toast) {
const focusable = toast.querySelectorAll('button, a, input');
const first = focusable[0];
const last = focusable[focusable.length - 1];
toast.addEventListener('keydown', function (e) {
if (e.key === 'Tab') {
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
}
});
}
Подключение:
onOpened: function (instance, toast) {
trapFocus(toast);
}
Для корректной работы экранных читалок необходимо добавить ARIA-атрибуты.
Пример:
iziToast.show({
title: 'Ошибка',
message: 'Произошла проблема',
attributes: {
role: 'alert',
'aria-live': 'assertive'
}
});
Основные значения:
role="alert" — сообщает о важном уведомленииaria-live="polite" — мягкое уведомлениеaria-live="assertive" — немедленное озвучиваниеАвтоматическое исчезновение уведомлений может мешать клавиатурной навигации. Для таких случаев рекомендуется отключить таймер:
iziToast.show({
timeout: false
});
Или увеличивать время:
timeout: 10000
Можно добавлять кастомные горячие клавиши для действий:
onOpened: function (instance, toast) {
toast.addEventListener('keydown', function (e) {
if (e.key === 'Enter') {
const btn = toast.querySelector('button');
if (btn) btn.click();
}
});
}
Это позволяет:
При наличии нескольких уведомлений важно определить:
Рекомендуемый подход:
Пример:
onOpened: function (instance, toast) {
const toasts = document.querySelectorAll('.iziToast');
const lastToast = toasts[toasts.length - 1];
if (toast === lastToast) {
const btn = toast.querySelector('button');
if (btn) btn.focus();
}
}
В сложных интерфейсах уведомления должны вписываться в общую систему фокуса. Для этого:
Пример возврата фокуса:
let lastFocused;
document.addEventListener('focusin', (e) => {
lastFocused = e.target;
});
iziToast.show({
onClosed: function () {
if (lastFocused) lastFocused.focus();
}
});
Фокус не устанавливается
onOpenedTab пропускает элементы
tabindexEscape не работает
stopPropagation или изменение порядка
подпискиНевозможно выйти из уведомления
Пример комплексной настройки:
iziToast.show({
title: 'Действие требуется',
message: 'Подтвердите операцию',
timeout: false,
close: false,
overlay: true,
buttons: [
['<button>Да</button>', function (instance, toast) {
instance.hide({}, toast);
}, true],
['<button>Нет</button>', function (instance, toast) {
instance.hide({}, toast);
}]
],
onOpened: function (instance, toast) {
trapFocus(toast);
const btn = toast.querySelector('button');
if (btn) btn.focus();
},
onClosed: function () {
document.body.focus();
},
attributes: {
role: 'alertdialog',
'aria-modal': 'true'
}
});
Такой подход превращает уведомление в полноценный доступный интерфейсный элемент, управляемый исключительно с клавиатуры.