Приклад модального вікна
Опис
Після появи модального вікна відбувається наступне:
Фокус ставиться на кнопку закриття модального вікна.
Заголовок і текст модального вікна автоматично озвучується скрінрідером.
В другу чергу скрінрідер озвучить текстову мітку кнопки.
При навігації клавіатурою фокус не може покинути модальне вікно. Поки модальне вікно існує, рух фокуса "закільцьований" у ньому.
Демонстрація
Протестувати
✖
Повідомлення
Цей текст має бути озвучений скрінрідером.
Зрозуміло
Відповідний код HTML
<button class ="button" id ="buttonWindowModalShow" > Протестувати</button>
<div id ="windowModal" role ="dialog" aria-modal ="true" aria-labelledby ="windowModalTitle" aria-describedby ="windowModalMessage" style ="display: none" >
<div id ="windowModalFog" > </div>
<div id ="windowModalInner" >
<button class ="button_no_borders" id ="buttonWindowModalHideCross" aria-label ="Закрити це повідомлення" > ✖</button>
<div style ="margin: 10px 0px 16px 0px" >
<h2 id ="windowModalTitle" > Повідомлення</h2>
<p id ="windowModalMessage" > Цей текст має бути озвучений скрінрідером.</p>
</div>
<div >
<button class ="button" id ="buttonWindowModalHide" > Зрозуміло</button>
</div>
</div>
</div>
Відповідний код JS
const windowModal = document.getElementById("windowModal");
const windowModalMessage = document.getElementById("windowModalMessage");
const buttonWindowModalShow = document.getElementById("buttonWindowModalShow");
const buttonWindowModalHide = document.getElementById("buttonWindowModalHide");
const buttonWindowModalHideCross = document.getElementById("buttonWindowModalHideCross");
buttonWindowModalShow.addEventListener("click", () => {
WindowModal.show();
});
buttonWindowModalHide.addEventListener("click", () => {
WindowModal.hide();
});
buttonWindowModalHideCross.addEventListener("click", () => {
WindowModal.hide();
});
document.addEventListener("keyup", function (event) {
console.log("keyup detected");
if (event.key === "Escape" && windowModal.style.display === "flex") {
WindowModal.hide();
}
});
class WindowModal
{
static show() {
console.log("WM.show was called");
windowModal.style.display = "flex";
buttonWindowModalHide.focus();
let focusableElementsSelector = "button, [href], input, select, textarea, [tabindex]:not([tabindex ='-1'])";
let focusableElements = windowModal.querySelectorAll(focusableElementsSelector);
this.focusableElementFirst = focusableElements[0];
this.focusableElementLast = focusableElements[focusableElements.length - 1];
windowModal.addEventListener("keydown", bindFocus);
}
static hide() {
console.log("WM.hide was called");
windowModal.style.display = "none";
buttonWindowModalShow.focus();
windowModal.removeEventListener("keydown", bindFocus);
}
}
function bindFocus(event) {
console.log("bindFocus was called");
let isTabPressed = event.key === "Tab";
if (!isTabPressed) return;
console.log("isTab");
if (event.shiftKey) {
if (document.activeElement === WindowModal.focusableElementFirst) {
event.preventDefault();
console.log("focusing at last element in modal window");
WindowModal.focusableElementLast.focus();
}
} else {
if (document.activeElement === WindowModal.focusableElementLast) {
event.preventDefault();
console.log("focusing at first element in modal window");
WindowModal.focusableElementFirst.focus();
}
}
}
Відповідний код CSS
p {
margin: 0px 0px 2px 0px;
padding: 0px;
}
#windowModal {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: fixed;
left: 0px;
right: 0px;
top: 0px;
bottom: 0px;
}
#windowModalFog {
background-color: #000;
opacity: 0.5;
position: absolute;
z-index: 1;
width: 100%;
height: 100%;
}
#windowModalInner {
background-color: #fff;
border-radius: 12px;
color: #000;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 20px;
position: relative;
max-width: 500px;
text-align: center;
z-index: 2;
}
#windowModalTitle {
margin: 0px 0px 8px 0px;
padding: 0px;
}
#buttonWindowModalHideCross {
font-size: 18px;
position: absolute;
right: 8px;
top: 8px;
}
.button_no_borders {
background: none;
border: 0px #000 solid;
}
button {
cursor: pointer;
}
button:focus {
outline: 2px solid #4444FF;
outline-offset: 2px;
}
.focusable:focus {
outline: 2px solid #4444FF;
outline-offset: 2px;
}