콘텐츠연합플랫폼
클라이언트개발부 지성봉
role="dialog"
사용자가 정보를 입력하거나 응답할 것을 요구하도록 유도하기 위해 어플리케이션의 현재 처리를 중단시키도록 설계된 어플리케이션 윈도우
<div class="popup-wrap">
<div class="popup-body"
role="dialog"
aria-labelledby="pop-title">
<strong id="pop-title" class="modal-header">접근 가능한 레이어 팝업</strong>
<div class="modal-body">
<p>
접근 가능한 레이어 팝업이란?
</p>
...
대화 상자 내부로 초점 이동
tabindex="-1"
을 추가하여 이 요소로 초점 이동
<div class="popup-wrap">
<div class="popup-body" role="dialog" aria-labelledby="pop-title">
<a class="placeholder" tabindex="-1"></a>
<strong id="pop-title" class="modal-header">접근 가능한 레이어 팝업</strong>
<div id="popup-contents" class="modal-body">
<p>
접근 가능한 레이어 팝업이란?
</p>
function openPopup () {
var popupBody = document.querySelector(".popup-body");
document.documentElement.classList.add("on-popup");
popupBody.querySelector(".placeholder").focus();
}
role="dialog"
가 자동으로 처리
단, NVDA 2017.4+, NVDA 2017.2 + FireFox, JAWS 18+ 등에서만 지원
aria-modal="true"
in ARIA 1.1
iOS 10.x/10.2에서는 문제가 있는 것으로 리포트 됨
(대화상자 제목과 지시사항들이 읽는 순서에 따라 접근 가능하지 않게 되는 문제 발생)
차선책: 대화 상자 외 타 콘텐츠에 aria-hidden="true"
설정
단, 마크업 순서에 따라 적용이 어려워지는 상황이 발생.
가급적 dialog 요소를 level 1 수준으로 위치시키는 것이 정신건강에 좋음
function setSiblingsHidden(currElem){
var ommits = ["script", "meta", "link", "style", "base"];
for(var i = -1, node; node = currElem.parentNode.children[++i];){
if(node == currElem || ommits.indexOf(node.tagName.toLowerCase()) > -1)
continue;
node.setAttribute("aria-hidden", "true");
node.setAttribute("data-outside-modal", "true");
}
}
function openPopup(){
var popupBody = document.querySelector(".popup-body");
document.documentElement.classList.add("on-popup");
setSiblingsHidden(document.querySelector(".popup-wrap"));
popupBody.querySelector(".placeholder").focus();
}
function unsetSiblingsHidden(currElem){
for(var i = -1,
node,
outsides= document.querySelectorAll("[data-outside-modal]");
node = outsides[++i]; ) {
node.removeAttribute("aria-hidden");
node.removeAttribute("data-outside-modal");
}
}
function closePopup(event){
event = event || window.event;
document.documentElement.classList.remove("on-popup");
unsetSiblingsHidden();
}
(function(){
var focuslock = (function(){
var firstElem, lastElem;
return {
setFirstBtn : function(el){
firstElem = el;
},
setLastBtn : function(el){
lastElem = el;
},
focuslockKeyDown : function(event){
event = event || window.event;
var keycode = event.which || event.keyCode;
if(event.shiftKey && keycode === 9 && event.target === firstElem){
event.preventDefault ? event.preventDefault() : event.returnValue = false;
lastElem.focus();
}else if(!event.shiftKey && keycode === 9 && event.target === lastElem){
event.preventDefault ? event.preventDefault() : event.returnValue = false;
firstElem.focus();
}
}
};
}());
window.focuslock = window.focuslock || focuslock;
}());
function openPopup(){
var popupBody = document.querySelector(".popup-body");
document.documentElement.classList.add("on-popup");
focuslock.setFirstBtn(btnClosePopup);
focuslock.setLastBtn(btnClosePopup);
popupBody.addEventListener("keydown", focuslock.focuslockKeyDown);
setSiblingsHidden(document.querySelector(".popup-wrap"));
popupBody.querySelector(".placeholder").focus();
}
function closePopup(event){
event = event || window.event;
var popupBody = document.querySelector(".popup-body");
document.documentElement.classList.remove("on-popup");
popupBody.removeEventListener("keydown", focuslock.focuslockKeyDown);
unsetSiblingsHidden();
}
팝업이 열릴 때 초점이 얻어진 요소를 기억해 두었다가 팝업이 닫힐 때 해당 요소에 다시 초점 이동
(function () {
var focusedElem = null;
var btnOpenPopup = document.getElementById("open-popup");
var btnClosePopup = document.getElementById("close-popup");
...
function openPopup(){
var popupBody = document.querySelector(".popup-body");
document.documentElement.classList.add("on-popup");
focusedElem = this;
focuslock.setFirstBtn(btnClosePopup);
focuslock.setLastBtn(btnClosePopup);
popupBody.addEventListener("keydown", focuslock.focuslockKeyDown);
...
}
function closePopup(event){
event = event || window.event;
var popupBody = document.querySelector(".popup-body");
...
focusedElem.focus();
}
}());
function openPopup(){
...
document.addEventListener("keydown", closePopup);
}
function closePopup(event){
event = event || window.event;
if(event.type === 'keydown' && event.keyCode !== 27){
return;
}
...
document.removeEventListener("keydown", closePopup);
}
Roles/Property | Description |
---|---|
dialog | 사용자가 정보를 입력하거나 응답할 것을 요구하도록 유도하기 위해 어플리케이션의 현재 처리를 중단시키도록 설계된 어플리케이션 윈도우 |
aria-label aria-labelledby | 해당 객체의 label(이름)을 설정 |
aria-describe aria-describedby | 해당 객체에 대한 설명 추가 |
Key | Behavior |
---|---|
Tab |
|
Shift + Tab |
|
Esc | 대화상자 닫기 |
감사합니다
NIAW AOA Github
publisher@publisher.name