在本文中,我想告诉您如何在不使用“ aria-modal”属性的情况下实现可访问的模式。
有点理论!
“区域模式”是用于告诉辅助技术(例如屏幕阅读器)当前对话框下的Web内容不可互操作(惰性)的属性。换句话说,模态下的任何元素都不能集中于单击,TAB / SHIFT + TAB导航或在传感器设备上滑动。
但是为什么我们不能在模态窗口中使用“ aria-modal”呢?
有以下几个原因:
- 只是屏幕阅读器不支持
- 被伪类“:之前/:之后”忽略
让我们继续执行。
实作
为了开始开发,我们需要选择可用模式窗口应具有的属性:
- 模态窗口之外的所有交互元素都必须被阻止以供用户操纵:单击,聚焦等...
- 导航应仅通过浏览器的系统组件和模式本身的内容才可用(模式窗口之外的所有内容都应忽略)
空白
让我们使用一个模板,以免在创建模态窗口的分步说明上浪费时间。
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=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;
}
JS:
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。
1.为了进行进一步的工作,请创建具有以下属性和方法的类“ modalWindow”:
- doc-页面文档。在其中我们建立一个模态窗口
- modal-模态窗口的容器
- InteractiveElementsList-交互式元素的数组
- blockElementsList-页面块元素的数组
- 构造函数-类的构造函数
- create-用于创建模式窗口的方法
- remove-用于删除模态的方法
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.第四步类似于步骤2,仅使用'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');
}
}
聚苯乙烯
如果在移动设备上无法解决文本元素导航的问题,则可以使用以下选择:
const BLOCKS_SELECTORS = ['div', 'header', 'main', 'section', 'footer'];
let children = this.doc.querySelectorAll(BLOCKS_SELECTORS .toString());