[JavaScript] 모달 팝업의 Focus Trapping과 WAI-ARIA

장유진·2023년 4월 29일
0

Implementation

목록 보기
4/4

1. 목표

팝업에서 사용할 수 있는 aria-* 속성을 적절히 사용하여 웹 접근성을 향상합니다.

모달 팝업에서 포커스 트래핑(Focus Trapping)을 구현하여 키보드 사용자가 실수로 팝업에서 벗어나지 않고 팝업 내부를 탐색할 수 있도록 합니다.

❗️모달 팝업이 오픈된 상태라면 tab을 눌렀을 때 팝업 내부 영역에서만 이동해야 합니다.

2. 구현 미리보기


3. 구현하기

HTML


  <h1 tabindex="0">Focus Trapping & WAI </h1>
  <button id="openPopupBtn" class="popup-open" aria-haspopup="true">Open Popup</button>

  <div id="popupContainer" class="popup" role="dialog" aria-modal="true" aria-labelledby="popupTitle" tabindex="-1">
    <div class="popup-content">
      <h2 id="popupTitle" tabindex="0">Popup Title</h2>
      <p tabindex="0">Popup content.</p>
      <input type="text" aria-label="input label" aria-required="true">
      <button id="closePopupBtn" class="popup-close">Close</button>
    </div>
  </div>
  <div id="modalOverlay" tabindex="-1"></div>
  • role : 이 속성은 해당 요소가 어떤 역할을 하고 있는지 스크린 리더에게 알려줍니다. 사용할 수 있는 속성은 다음과 같습니다.(https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques)

  • tabindex
    tabindex 속성이 없어도 키보드로 탐색할 수 있는 태그(input, select, textarea, a, button…)가 아니라면 tabindex="0" 속성을 사용해서 사용자가 Tab 키를 눌렀을 때 키보드 탐색 시 요소에 포커스 할 수 있도록 만들 수 있습니다.
    반대로, tabindex="-1" 을 지정하면 해당 요소를 키보드 탐색에서 제외시킬 수 있습니다.

  • aria-haspopup : 이 속성은 요소에 팝업되는 메뉴 또는 다이얼로그가 있음을 의미합니다.

  • aria-modal : 이 속성을 사용하면 모달 팝업 내에서만 콘텐츠 탐색을 하도록 스크린 리더에게 알려줄 수 있습니다. 단 모든 스크린 리더가 이 속성을 지원하지 않다는 것을 유의해야 합니다.

  • aria-labelledby : 이 속성은 현재 요소에 설명을 제공하고 싶을 때, 참조할 요소의 ID값을 제공합니다.
    위 코드에서는 팝업 컨테이너의 레이블로 팝업의 이름을 사용합니다.

  • aria-label : 이 속성은 스크린 리더에게 현재 요소의 레이블을 전달합니다.

  • aria-required : 이 속성은 해당 입력이 필수값임을 스크린 리더에게 알려줍니다.

JavaScript

팝업 내의 포커스 가능한 요소 목록과 첫 번째 및 마지막 포커스 가능한 요소를 정의합니다.
tab을 누르면 다음 요소로 포커스가 이동하고, shift + tab 을 누르면 이전 요소로 포커스가 이동합니다.
사용자가 팝업 콘텐츠에서 탭을 누르면 포커스 가능한 첫 번째 또는 마지막 요소에 도달했는지 확인해서 만약 팝업의 첫번째 요소에서 shift + tab을 눌렀을 경우 팝업의 마지막 요소로 focus를 이동하고, 마지막 요소에서 tab을 눌렀을 경우 팝업의 첫번째 요소로 focus를 이동합니다.


  const focusableElements =
    'a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, *[tabindex], *[contenteditable]';
  const modal = document.querySelector('#popupContainer');
  const focusableContent = modal.querySelectorAll(focusableElements);
  const firstFocusableElement = modal.querySelectorAll(focusableElements)[0];
  const lastFocusableElement = focusableContent[focusableContent.length - 1];

  const input = document.querySelector('input');
  const openPopupBtn = document.querySelector('#openPopupBtn');
  const closePopupBtn = document.querySelector('#closePopupBtn');
  const modalOverlay = document.querySelector('#modalOverlay');

  const togglePopup = (open) => {
    if (open) {
      modal.style.display = 'block';
      modalOverlay.style.display = 'block';
      input.focus();

    } else {
      modal.style.display = 'none';
      modalOverlay.style.display = 'none';
    }
  };

  document.addEventListener('keydown', (e) => {
    let isTabPressed = e.key === 'Tab' || e.keyCode === 9;

    if (!isTabPressed) {
      return;
    }

    if (e.shiftKey) {
      if (document.activeElement === firstFocusableElement) {
        lastFocusableElement.focus();
        e.preventDefault();
      }
    } else {
      if (document.activeElement === lastFocusableElement) {
        firstFocusableElement.focus();
        e.preventDefault();
      }
    }
  });

  openPopupBtn.addEventListener('click', () => togglePopup(true));
  closePopupBtn.addEventListener('click', () => togglePopup(false));

Reference

① Add Focus To Pop Up / Modal On Click For Tabbing / Accessibility - JavaScript
https://stackoverflow.com/questions/72006912/add-focus-to-pop-up-modal-on-click-for-tabbing-accessibility-javascript

② How to trap focus inside modal to make it ADA compliant
https://uxdesign.cc/how-to-trap-focus-inside-modal-to-make-it-ada-compliant-6a50f9a70700

0개의 댓글