Всем привет! В этой статье я хотел бы рассказать вам, как реализовать доступный модальное окно без использования атрибута "ариа-модальный" .
Немного теории!
«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-
Документ, Который Вы Ждали
19 Oct, 24 -
Частный Фильтр
19 Oct, 24