Тултип — это небольшой всплывающий элемент интерфейса, который отображает поясняющую информацию при взаимодействии пользователя с элементом. Чаще всего тултипы появляются при наведении курсора мыши или при получении элементом фокуса с клавиатуры. В контексте доступности (accessibility) важно обеспечивать корректное отображение тултипов не только для пользователей мыши, но и для тех, кто перемещается по интерфейсу с помощью клавиатуры или вспомогательных технологий.
Библиотека ally.js предоставляет набор утилит для управления фокусом, навигацией и доступностью интерфейсов. При реализации тултипов она используется для корректной обработки состояний фокуса, определения активного элемента и обеспечения предсказуемого поведения при взаимодействии с различными способами ввода.
При реализации тултипов необходимо учитывать несколько принципов:
1. Поддержка клавиатурной навигации Информация, отображаемая в тултипе, должна быть доступна при фокусе на элементе, а не только при наведении курсора.
2. Управление фокусом Тултип не должен перехватывать фокус, если он не является интерактивным элементом.
3. Связь с целевым элементом Элемент, вызывающий
тултип, должен быть связан с ним через атрибут
aria-describedby.
4. Контроль видимости Тултип должен появляться и исчезать предсказуемо, без неожиданных изменений состояния интерфейса.
Библиотека ally.js помогает корректно обрабатывать события фокуса и взаимодействия, обеспечивая согласованность поведения.
Базовая структура включает два элемента: триггер и сам тултип.
<button id="info-button" aria-describedby="tooltip-info">
Информация
</button>
<div id="tooltip-info" role="tooltip" hidden>
Дополнительные сведения о действии кнопки
</div>
Ключевые моменты:
role="tooltip" сообщает вспомогательным технологиям
назначение элемента.aria-describedby связывает кнопку с тултипом.hidden используется для скрытия элемента до
момента активации.Классическая модель поведения тултипа основана на событиях
mouseenter и mouseleave.
const button = document.getElementById('info-button');
const tooltip = document.getElementById('tooltip-info');
button.addEventListener('mouseenter', () => {
tooltip.hidden = false;
});
button.addEventListener('mouseleave', () => {
tooltip.hidden = true;
});
Однако такой подход не охватывает пользователей клавиатуры. Поэтому необходимо дополнительно обрабатывать события фокуса.
Фокус возникает при переходе по интерфейсу с помощью клавиши Tab или программного управления фокусом. В этом случае тултип должен появляться аналогично наведению мыши.
button.addEventListener('focus', () => {
tooltip.hidden = false;
});
button.addEventListener('blur', () => {
tooltip.hidden = true;
});
Таким образом обеспечивается единая модель поведения для разных способов взаимодействия.
Библиотека ally.js предоставляет инструменты для определения текущего активного элемента и контроля навигации.
Подключение библиотеки:
<script src="https://unpkg.com/ally.js/ally.min.js"></script>
Одной из полезных утилит является observer активного элемента.
const observer = ally.observe.activeElement();
observer.subscribe(function(event) {
console.log('Текущий элемент фокуса:', event.activeElement);
});
Этот механизм позволяет отслеживать изменения фокуса и реагировать на них централизованно.
При наличии большого количества тултипов удобнее использовать единый механизм наблюдения.
const tooltipMap = new Map([
['info-button', 'tooltip-info']
]);
const observer = ally.observe.activeElement();
observer.subscribe(event => {
const id = event.activeElement && event.activeElement.id;
tooltipMap.forEach((tooltipId, buttonId) => {
const tooltip = document.getElementById(tooltipId);
if (id === buttonId) {
tooltip.hidden = false;
} else {
tooltip.hidden = true;
}
});
});
Преимущества такого подхода:
Важной задачей является различение hover и focus взаимодействий. Например, пользователь может использовать клавиатуру, сенсорный экран или мышь.
В ally.js существует модуль определения ввода:
const interaction = ally.when.focusable();
interaction.then(element => {
console.log('Элемент готов к получению фокуса:', element);
});
Это позволяет корректно активировать тултипы только для элементов, которые действительно могут получать фокус.
Позиция тултипа должна соответствовать расположению элемента. Для
этого часто используется вычисление координат через
getBoundingClientRect.
function positionTooltip(trigger, tooltip) {
const rect = trigger.getBoundingClientRect();
tooltip.style.position = 'absolute';
tooltip.style.top = rect.bottom + window.scrollY + 'px';
tooltip.style.left = rect.left + window.scrollX + 'px';
}
Вызов функции при отображении:
button.addEventListener('focus', () => {
tooltip.hidden = false;
positionTooltip(button, tooltip);
});
Такое позиционирование делает поведение тултипа более устойчивым при прокрутке страницы.
В интерфейсах с большим количеством элементов удобно использовать делегирование событий.
document.addEventListener('focusin', event => {
const trigger = event.target.closest('[data-tooltip]');
if (!trigger) return;
const tooltip = document.getElementById(trigger.dataset.tooltip);
tooltip.hidden = false;
});
document.addEventListener('focusout', event => {
const trigger = event.target.closest('[data-tooltip]');
if (!trigger) return;
const tooltip = document.getElementById(trigger.dataset.tooltip);
tooltip.hidden = true;
});
HTML-разметка:
<button data-tooltip="tooltip-save">Сохранить</button>
<div id="tooltip-save" role="tooltip" hidden>
Сохраняет текущие изменения
</div>
Этот подход значительно уменьшает количество обработчиков.
Некоторые ошибки при реализации тултипов могут ухудшать доступность интерфейса:
Перехват фокуса тултипом Если тултип получает фокус, пользователь может потерять контекст навигации.
Мгновенное исчезновение Слишком быстрое скрытие тултипа при небольшом смещении курсора.
Отсутствие задержки появления Мгновенные всплывающие элементы могут мешать чтению интерфейса.
Пример реализации задержки:
let showTimer;
button.addEventListener('mouseenter', () => {
showTimer = setTimeout(() => {
tooltip.hidden = false;
}, 300);
});
button.addEventListener('mouseleave', () => {
clearTimeout(showTimer);
tooltip.hidden = true;
});
На практике тултип должен поддерживать оба сценария взаимодействия.
function showTooltip() {
tooltip.hidden = false;
}
function hideTooltip() {
tooltip.hidden = true;
}
button.addEventListener('mouseenter', showTooltip);
button.addEventListener('mouseleave', hideTooltip);
button.addEventListener('focus', showTooltip);
button.addEventListener('blur', hideTooltip);
Такой подход обеспечивает:
Перед назначением тултипа полезно определить, может ли элемент получать фокус.
if (ally.is.focusable(button)) {
console.log('Элемент доступен для клавиатурной навигации');
}
Это особенно важно при работе с нестандартными элементами интерфейса,
созданными на основе div или span.
В сложных интерфейсах тултипы могут:
Пример обработки клавиши Escape:
document.addEventListener('keydown', event => {
if (event.key === 'Escape') {
tooltip.hidden = true;
}
});
Одной из ключевых задач ally.js является поддержка предсказуемого управления фокусом. В сочетании с тултипами это позволяет:
Тултипы становятся не просто декоративным элементом интерфейса, а частью системы доступности, обеспечивающей пользователю контекстную информацию независимо от способа взаимодействия.