WAI-ARIA를 이용해 접근성을 개선하고, tabindex를 이용해 팝업 내부 영역에서만 이동하도록 하는 방법을 사용한 팝업 만들기
<button id="popupButton" aria-haspopup="true" aria-expanded="false">팝업 열기</button>
<div id="popup" role="dialog" aria-labelledby="popupHeading" aria-modal="true" tabindex="-1">
<h2 id="popupHeading">팝업 제목</h2>
<p>팝업 내용</p>
<input type="text" placeholder="입력1" />
<input type="text" placeholder="입력2" />
<input type="text" placeholder="입력3" />
<button id="closeButton">닫기</button>
</div>
aria-haspopup
는 WAI-ARIA(웹 접근성 개선을 위한 인터랙티브 웹 애플리케이션) 스펙의 일부로, 요소가 팝업이나 메뉴와 같은 하위 항목을 가지고 있음을 나타내는 속성이다. 이 속성은 스크린 리더 등 보조 기술을 사용하는 사용자에게 해당 요소에 대한 상태를 알려줌으로써 접근성을 개선한다.
aria-haspopup
속성
- 값
true
는 해당 요소가 하위 팝업이나 메뉴를 가지고 있다는 것을 나타낸다.- 값
false
는 해당 요소가 하위 팝업이나 메뉴를 가지고 있지 않음을 나타낸다.
aria-expanded
는 특정 요소가 접혀 있는지(축소 상태) 또는 펼쳐져 있는지(확장 상태)를 나타내는 속성이다.
aria-expanded
속성
- 값
true
는 해당 요소가 펼쳐져 있음을 나타낸다.- 값
false
는 해당 요소가 접혀 있음을 나타낸다.
팝업창이 펼쳐지면 aria-expanded
속성의 값을 true
로 변경하면 된다.
aria-modal
은 특정 요소가 모달 대화 상자(모달 창)임을 나타내는 속성이다. 모달 창은 사용자의 주의를 요구하고, 배경 컨텐츠와의 상호작용을 제한하여 모달이 닫힐 때까지 사용자의 주의를 돌리지 않도록 한다.
aria-modal
속성
- 값
true
는 해당 요소가 모달 창임을 나타낸다.- 값
false
는 해당 요소가 모달 창이 아님을 나타낸다.
위 예시에서 aria-modal='true'
속성은 role='dialog'
가 설정된 요소가 모달 창 임을 나타낸다.
팝업 내부의 요소들에게 tabindex를 할당하여 이동 가능하도록 설정한다.
const popupButton = document.getElementById('popupButton');
const popup = document.getElementById('popup');
const closeButton = document.getElementById('closeButton');
const popupContents = popup.querySelectorAll('a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])');
const enableTabindex = () => {
popupContents.forEach((content) => {
content.setAttribute('tabindex', '0');
})
}
const disableTabindex = () => {
popupContents.forEach((content) => {
content.removeAttribute('tabindex');
})
}
popupButton.addEventListener('click', () => {
popup.style.display = 'block';
popupButton.setAttribute('aria-expanded', 'true');
enableTabindex();
closeButton.focus();
});
closeButton.addEventListener('click', () => {
popup.style.display = 'none';
popupButton.setAttribute('aria-expanded', 'false');
disableTabindex();
});
//tabindex 설정
popup.addEventListener('keydown', (e) => {
const lastElement = popupContents[popupContents.length - 1];
if (e.key === 'Tab') {
if (e.target === lastElement) {
e.preventDefault();
popupContents[0].focus();
}
}
});
위 예시에서 팝업 버튼을 클릭하면 aria-expanded
속성을 변경하여 팝업이 열렸음을 알리고, 팝업 닫기 버튼을 클릭하면 팝업이 닫힌다.
팝업이 열렸을 경우, enableTabinx()
함수를 호출하여 팝업 내부의 포커스 가능한 요소들에게 tabindex를 설정한다. 팝업이 닫힐 때는 disableTabindex()
함수를 호출하여 tabindex 값을 초기 상태로 복원한다.
JavaScript를 사용하여 동적으로 tabindex를 적용하면 팝업이 열릴 때만 포커스 가능한 요소에 접근할 수 있게 되며, 팝업이 닫힐 때 tabindex는 제거되어 탭 키 이동의 순서에 영향을 주지 않는다.
Focus
웹은 사용자와 상호작용(interactive)이 가능한 요소는 기본적으로 키보드로 포커스가 잡히게 되어 있다. 대표적으로
<input>
,<select>
,<button>
,<a>
,<textarea>
가 있다.
tabindex
👉🏻 tabindex는 HTML 요소의 키보드 포커스를 지정하는 데 사용되는 속성이다. 이 속성을 사용하면 사용자가 탭 키를 사용하여 요소 간에 이동할 수 있다.
tabindex 속성
- 양의 정수(0보다 큰 값): 양의 정수를 지정하면 해당 요소가 탭 키 순서에 따라 포커스를 받을 수 있다. 작은 값부터 큰 값의 순서로 요소에 포커스가 이동한다. (접근성을 고려하면 권장되는 방법이 아니다.)
- 0: 0을 지정하면 요소는 탭 키를 사용하여 포커스를 받을 수 있지만, 탭 키 순서는 요소의 위치에 따라 결정된다. 일반적으로 이 값을 사용하여 정적인 요소를 탭 순서에 추가한다.
- 음의 정수(-1): 음의 정수를 지정하면 사용자는 탭 키를 눌러도 접근할 수 없으나, JavaScript에서
focus()
메서드를 호출해 포커스를 부여할 수 있다.