Решение Проблемы Сделать Модальное Окно Доступным Для Людей С Ограниченными Возможностями

Всем привет! В этой статье я хотел бы рассказать вам, как реализовать доступный модальное окно без использования атрибута "ариа-модальный" .



Немного теории!

«aria-modal» — это атрибут, используемый для сообщения вспомогательным технологиям (например, программам чтения с экрана) о том, что веб-контент в текущем диалоговом окне недоступен для взаимодействия (инертный).

Другими словами, ни один элемент ниже модального окна не должен получать фокус при нажатии, навигации с помощью TAB/SHIFT+TAB или пролистывании на сенсорных устройствах.

Но почему мы не можем использовать «aria-modal» для модального окна? Есть несколько причин: просто не поддерживается программами чтения с экрана игнорируется псевдоклассами ":before/:after" Перейдем к реализации.



Выполнение

Для того, чтобы начать разработку, нам необходимо выделить свойства, которыми она должна обладать.

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

навигация должна быть доступна только между системными компонентами браузера и содержимым самого модального окна (весь контент за пределами модального окна должен игнорироваться)

Пустой

Воспользуемся шаблоном, чтобы не тратить время на пошаговое описание создания модального окна.

HTML:

  
  
  
  
  
  
  
  
  
  
  
  
   

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="ширина = ширина устройства, начальный масштаб = 1,0"> <title>Document</title> </head> <body> <div> <button type="button" id="infoBtn" class="btn"> Standart button </button> <button type="button" id="openBtn"> Open modal window</button> <div role="button" tabindex="0" id="infoBtn" class="btn"> Custom button </button> </div> <div> Lorem, ipsum dolor sit amet consectetur adipisicing elit. Deserunt maxime tenetur sint porro tempore aperiam! Eaque tempore repudiandae culpa omnis placeat, fugit nostrum quisquam in ipsa odit accusamus illum velit? </div> <div id="modalWindow" class="modal"> <div> <button type="button" id="closeBtn" class="btn-close">Close</button> <h2>Modal window</h2> <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Expedita, doloribus.</p> </div> </div> </body> </html>

Стили:

.

modal { position: fixed; font-family: Arial, Helvetica, sans-serif; top: 0; right: 0; bottom: 0; left: 0; background: rgba(0,0,0,0.8); z-index: 99999; transition: opacity 400ms ease-in; display: none; pointer-events: none; } .

active{ display: block; pointer-events: auto; } .

modal > div { width: 400px; position: relative; margin: 10% auto; padding: 5px 20px 13px 20px; border-radius: 10px; background: #fff; } .

btn-close { padding: 5px; position: absolute; right: 10px; border: none; background: red; color: #fff; box-shadow: 0 0 10px rgba(0,0,0,0.5); } .

btn { display: inline-block; border: 1px solid #222; padding: 3px 10px; background: #ddd; box-sizing: border-box; }

ЯС:

let modaWindow = document.getElementById('modalWindow'); document.getElementById('openBtn').

addEventListener('click', function() { modaWindow.classList.add('active'); }); document.getElementById('closeBtn').

addEventListener('click', function() { modaWindow.classList.remove('active'); });

Если открыть страницу и попытаться перейти к элементам, расположенным за модальным окном, с помощью клавиш «TAB/SHIFT+TAB», то эти элементы получают фокус, как показано на прикрепленном рисунке.



Решение проблемы сделать модальное окно доступным для людей с ограниченными возможностями

Чтобы решить эту проблему, нам нужно присвоить атрибут tabindex всем интерактивным элементам со значением минус один.

1. Для дальнейшей работы создайте класс modalWindow со следующими свойствами и методами: doc — документ страницы, в которой мы строим модальное окно modal — контейнер модального окна activeElementsList — массив интерактивных элементов blockElementsList - массив элементов блока страницы конструктор — конструктор класса create — метод, используемый для создания модального окна удалить - метод, используемый для удаления модального окна 2. Реализуем конструктор:

constructor(doc, modal) { this.doc = doc; this.modal = modal; this.interactiveElementsList = []; this.blockElementsList = []; }

«interactiveElementsList» и «blockElementsList» необходимы для хранения элементов страницы, которые были изменены при создании модального окна.

3. Создайте константу для хранения элементов, которые могут иметь фокус:

const INTERECTIVE_SELECTORS = ['a', 'button', 'input', 'textarea', '[tabindex]'];

4. В методе «создать» выберите все элементы, соответствующие нашим селекторам, и установите для всех значение «tabindex=-1» (игнорируя элементы, которые уже имеют это значение).



let elements = this.doc.querySelectorAll(INTERECTIVE_SELECTORS.toString()); let element; for (let i = 0; i < elements.length; i++) { element = elements[i]; if (!this.modal.contains(element)) { if (element.getAttribute('tabindex') !== '-1') { element.setAttribute('tabindex', '-1'); this.interactiveElementsList.push(element); } } }

Аналогичная проблема возникает, когда мы используем для навигации специальные клавиши или жесты (в мобильных программах), в этом случае мы можем перемещаться не только по интерактивным элементам, но и по текстовым.

5. Здесь нам не нужно создавать массив для хранения селекторов, мы просто возьмем всех дочерних элементов узла 'body'.



let children = this.doc.body.children;

6. Шестой шаг аналогичен шагу № 4, только с использованием «aria-hidden»

for (let i = 0; i < children.length; i++) { element = children[i]; if (!this.modal.contains(element)) { if (element.getAttribute('aria-hidden') !== 'true') { element.setAttribute('aria-hidden', 'true'); this.blockElementsList.push(element); } } }

Завершенный метод "создать":

create() { let elements = this.doc.querySelectorAll(INTERECTIVE_SELECTORS.toString()); let element; for (let i = 0; i < elements.length; i++) { element = elements[i]; if (!this.modal.contains(element)) { if (element.getAttribute('tabindex') !== '-1') { element.setAttribute('tabindex', '-1'); this.interactiveElementsList.push(element); } } } let children = this.doc.body.children; for (let i = 0; i < children.length; i++) { element = children[i]; if (!this.modal.contains(element)) { if (element.getAttribute('aria-hidden') !== 'true') { element.setAttribute('aria-hidden', 'true'); this.blockElementsList.push(element); } } } }

7. На седьмом шаге мы реализуем обратный метод create:

remove() { let element; while(this.interactiveElementsList.length !== 0) { element = this.interactiveElementsList.pop(); element.setAttribute('tabindex', '0'); } while(this.interactiveElementsList.length !== 0) { element = this.interactiveElementsList.pop(); element.setAttribute('aria-gidden', 'false'); } }

8. Чтобы это работало, нам нужно создать экземпляр класса «modalWindow» и вызвать методы «create» и «remove»:

let modaWindow = document.getElementById('modalWindow'); const modal = new modalWindow(document, modaWindow); document.getElementById('openBtn').

addEventListener('click', function() { modaWindow.classList.add('active'); // modal.create(); }); document.getElementById('closeBtn').

addEventListener('click', function() { modaWindow.classList.remove('active'); // modal.remove(); });

Полный код класса:

class modalWindow{ constructor(doc, modal) { this.doc = doc; this.modal = modal; this.interactiveElementsList = []; this.blockElementsList = []; } create() { let elements = this.doc.querySelectorAll(INTERECTIVE_SELECTORS.toString()); let element; for (let i = 0; i < elements.length; i++) { element = elements[i]; if (!this.modal.contains(element)) { if (element.getAttribute('tabindex') !== '-1') { element.setAttribute('tabindex', '-1'); this.interactiveElementsList.push(element); } } } let children = this.doc.body.children; for (let i = 0; i < children.length; i++) { element = children[i]; if (!this.modal.contains(element)) { if (element.getAttribute('aria-hidden') !== 'true') { element.setAttribute('aria-hidden', 'true'); this.blockElementsList.push(element); } } } } remove() { let element; while(this.interactiveElementsList.length !== 0) { element = this.interactiveElementsList.pop(); element.setAttribute('tabindex', '0'); } while(this.interactiveElementsList.length !== 0) { element = this.interactiveElementsList.pop(); element.setAttribute('aria-gidden', 'false'); } }



P.S.

Если на мобильных устройствах не решены проблемы с навигацией по текстовым элементам, то можно воспользоваться следующим выбором:

const BLOCKS_SELECTORS = ['div', 'header', 'main', 'section', 'footer']; let children = this.doc.querySelectorAll(BLOCKS_SELECTORS .

toString());



Ссылки на полезные ресурсы

www.w3.org/TR/wai-aria www.w3.org/TR/WCAG21 Developer.mozilla.org/ru/docs/Web/HTML/Global_attributes/tabindex Теги: #JavaScript #HTML #доступность #хаки JavaScript
Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.