next js 어나운서

import { useRef } from 'react';

export function useA11yAnnouncer() {
  const timer = useRef<number | null>(null);

  const announce = async (message: string) => {
    const style = `border: 0; padding: 0; margin: 0; position: absolute !important;
      height: 1px; width: 1px; overflow: hidden;
      clip: rect(1px 1px 1px 1px); clip: rect(1px, 1px, 1px, 1px); clip-path: inset(50%); white-space: nowrap;`.replaceAll(
      /\n/g,
      ''
    );
    const appendAndAnnounce = async (element: Element, message: string) => {
      return new Promise(resolve => {
        if (!element.querySelector("[name='p_announceForAccessibility']")) {
          const div = document.createElement('div');
          div.setAttribute('name', 'div_announceForAccessibility');
          div.setAttribute('style', style);
          div.innerHTML = '<p aria-live="polite" name="p_announceForAccessibility"></p>';
          element.appendChild(div);
        }
        const pElement = element.querySelector<HTMLParagraphElement>("[name='p_announceForAccessibility']");

        if (!pElement) {
          return;
        }

        pElement.innerText = '';
        setTimeout(() => {
          pElement.innerText = message;
          resolve(void 0);
        }, 200);
      });
    };
    const announceMessages = async () => {
      if (typeof window !== 'undefined') {
        const bodyElement = document.body;
        const dialogElements = document.body.querySelectorAll<HTMLDialogElement>(
          '[role="dialog"][aria-modal="true"], dialog'
        );
        await appendAndAnnounce(bodyElement, message);

        dialogElements.forEach(element => {
          appendAndAnnounce(element, message);
        });
      }
    };

    await announceMessages();

    timer.current = window.setTimeout(removeAnnounce, 1000);
  };

  const removeAnnounce = () => {
    if (typeof window !== 'undefined') {
      const divElements = document.body.querySelectorAll<HTMLDivElement>("[name='div_announceForAccessibility']");

      if (!divElements || !timer.current) {
        return;
      }

      divElements.forEach(element => {
        element.parentNode?.removeChild(element);
      });

      clearTimeout(timer.current);
      timer.current = null;
    }
  };
  return [announce, removeAnnounce] as const;
}

Comments

답글 남기기