Спасибо за ответы на вопросы, надеюсь, в следующем объяснении будет показано, почему я их спрашивал (а затем я предложу несколько решений).
Почему мы не можем просто перехватить вкладку клавиша?
Пользователи программы чтения с экрана не используют навигацию только с помощью клавиши табуляции. В зависимости от используемого ими средства чтения с экрана они используют разные сочетания клавиш для навигации по заголовкам, ссылкам, формам и т. Д. c.
Это вызывает проблемы с доступностью всплывающих окон, так как люди, как правило, получают только клавишу tab . Затем, если пользователь использует ярлык, например 2 в NVDA, для перехода через заголовки 2-го уровня на странице, он может оказаться за пределами модального режима, не зная, что он существует, часто без какого-либо способа вернуться в модальный режим. без вкладок целую вечность.
Таким образом, решение очевидно, убедитесь, что ничего на странице не доступно (а не просто не фокусируется).
Однако для того, чтобы сделать это управляемым, вам необходимо упорядочить / организовать структуру DOM.
Проблемы, которые необходимо решить
- Пользователи программы чтения с экрана могут получить доступ к элементам без фокусировки
- Они могут менять свои сочетания клавиш, поэтому мы не можем полагаться на перехват нажатий клавиш, чтобы попытаться решить проблему.
- Мы хотим поддерживать тот же визуальный дизайн (т.е. мы не можем просто используйте
display:none
для всех остальных элементов). - Нам нужен шаблон, который мы можем повторить, чтобы мы не могли просто индивидуально скрыть элементы на странице.
- Мы хотим правильно управлять фокусировкой, чтобы когда модальное окно закрыто, оно возвращает фокус к предыдущему элементу (в ваших обстоятельствах).
- Мы хотим l oop вернуться к первому элементу в модальном режиме после достижения последнего элемента (мы можем сделать это перехватывать клавишу tab , поскольку мы не можем охватить все сценарии ios, и мы не хотим, поскольку это вызовет больше проблем с доступностью.)
Solution
задач 1, 2, 3 и 4
Поскольку мы не можем перехватывать нажатия клавиш, чтобы управлять фокусировкой в модале, мы должны сделать все остальные элементы (кроме тех, которые находятся в модале) полностью недоступными, пока модал активен.
aria-hidden="true"
эффективно display: none
для программ чтения с экрана. Поддержка aria-hidden
хороша на уровне от 90% до 95% для всех комбинаций программ чтения с экрана и браузера.
Чтобы сделать контент вне модального доступа недоступным, нам нужно добавить aria-hidden="true"
к каждому элементу вне модальный, а также tabindex="-1"
, чтобы гарантировать, что ничто не может быть сфокусировано вне модального режима с помощью клавиши tab .
Я спросил о структуре вашего документа, так как самый простой способ реализовать это на регионы / основные ориентиры.
Поэтому, когда модал активен, нам нужно добавить aria-hidden="true"
и tabindex="-1"
к <head>
, <main>
, <footer>
и c. Делая это на уровне ориентира и помещая модальное положение вне основного потока документов, становится легко управлять и поддерживать, сохраняя разметку semanti c HTML. Противоположное верно для модального (поэтому скрывайте его, используя ту же технику, когда он не активен.)
До открытия модального
<head aria-hidden="false"></head>
<main aria-hidden="false"></main>
<footer aria-hidden="false"></footer>
<div class="modal" aria-hidden="true" tabindex="-1"></div>
Модальное открытие
<head aria-hidden="true" tabindex="-1"></head>
<main aria-hidden="true" tabindex="-1"></main>
<footer aria-hidden="true" tabindex="-1"></footer>
<div class="modal" aria-hidden="false"></div>
Обратите внимание, что я всегда добавляю aria-hidden
, поскольку некоторые программы чтения с экрана плохо реагируют на динамическое добавление c aria
(хотя они хорошо реагируют на изменение свойств) .
Точки 5 и 6
Для этого я думаю, что будет проще поделиться кодом, который я использую для захвата фокуса в модале.
Цель функции ниже должен фокусировать первый фокусируемый элемент в модале при его открытии, сохранить ссылку на элемент, который активировал модал (как мы хотим вернуть пользователя туда, когда модал закрывается) и управлять фокусом.
Пожалуйста, обратите внимание, что я использую микробиблиотеку, чтобы включить селекторы стиля jQuery, поэтому вам может понадобиться настроить вещи для вашего использования.
Управление объяснением фокуса и кодом
Переменная item
является ссылкой кнопка, которая была нажата перед операцией ning модальный (чтобы мы могли вернуть фокус после закрытия модального).
Переменная className
- это имя класса модала, поэтому вы можете использовать разные модалы.
kluio.helpers
- это просто массив функций, которые я использую на сайте, поэтому их можно опустить.
kluio.globalVars
- это массив глобальных переменных, поэтому его можно заменить на возврат результатов из функции.
Я добавил комментарии к каждой части, чтобы объяснить, что она делает.
Функция setFocus
вызывается, когда модал открывается, передавая элемент, который был нажат, чтобы активировать его, и модал className
(работает для нашего варианта использования лучше, вместо него можно использовать идентификатор).
var kluio = {};
kluio.helpers = {};
kluio.globalVars = {};
kluio.helpers.setFocus = function (item, className) { //we pass in the button that activated the modal and the className of the modal, your modal must have a unique className for this to work.
className = className || "content"; //defaults to class 'content' in case of error ("content" being the class on the <main> element.)
kluio.globalVars.beforeOpen = item; //we store the button that was pressed before the modal opened in a global variable so we can return focus to it on modal close.
var focusableItems = ['a[href]', 'area[href]', 'input:not([disabled])', 'select:not([disabled])', 'textarea:not([disabled])', 'button:not([disabled])', '[tabindex="0"]']; //a list of items that should be focusable.
var findItems = [];
for (i = 0, len = focusableItems.length; i < len; i++) {
findItems.push('.' + className + " " + focusableItems[i]); //add every focusable item to an array.
}
var findString = findItems.join(", ");
kluio.globalVars.canFocus = Array.prototype.slice.call($('body').find(findString)); //please note we use a custom replacement for jQuery, pretty sure .find() behaves identically but just check it yourself.
if (kluio.globalVars.canFocus.length > 0) {
setTimeout(function () { //set timeout not needed most of the time, we have a modal that is off-screen and slides in, setting focus too early results in the page jumping so we added a delay, you may be able to omit this.
kluio.globalVars.canFocus[0].focus(); //***set the focus to the first focusable element within the modal
kluio.globalVars.lastItem = kluio.globalVars.canFocus[kluio.globalVars.canFocus.length - 1]; //we also store the last focusable item within the modal so we can keep focus within the modal.
}, 600);
}
}
Затем мы перехватываем событие keydown
с помощью следующей функции для управления фокусом.
document.onkeydown = function (evt) {
evt = evt || window.event;
if (evt.keyCode == 27) {
closeAllModals(); //a function that will close any open modal with the Escape key
}
if (kluio.globalVars.modalOpen && evt.keyCode == 9) { //global variable to check any modal is open and key is the tab key
if (evt.shiftKey) { //also pressing shift key
if (document.activeElement == kluio.globalVars.canFocus[0]) { //the current element is the same as the first focusable element
evt.preventDefault();
kluio.globalVars.lastItem.focus(); //we focus the last focusable element as we are reverse tabbing through the items.
}
} else {
if (document.activeElement == kluio.globalVars.lastItem) { //when tabbing forward we look for the last tabbable element
evt.preventDefault();
kluio.globalVars.canFocus[0].focus(); //move the focus to the first tabbable element.
}
}
}
};
Наконец, в вашей версии функции closeAllModals вам необходимо вернуть фокус на ссылающийся элемент / кнопку.
if (kluio.globalVars.beforeOpen) {
kluio.globalVars.beforeOpen.focus();
}
Строка kluio.globalVars.canFocus[0].focus();
вызывается для установки фокуса на первый фокусируемый элемент в модале после его активации, вам не нужно переходить на первый элемент при его открытии. ns это должно быть автоматически сфокусировано.
Точка 5 покрыта линией kluio.globalVars.beforeOpen = item;
, чтобы установить ссылку на элемент, который открыл модал, и kluio.globalVars.beforeOpen.focus();
в функции закрытия, чтобы вернуть фокус на этот элемент.
Пункт 6 охватывается функцией document.onkeydown
, начиная с if (kluio.globalVars.modalOpen && evt.keyCode == 9) {
.
Надеюсь, все вышеперечисленное понятно, любые вопросы просто задайте, если у меня будет время, я переверну его все в скрипке.