React Academy · レッスン

モーダルのフォーカスとキーボードトラップを扱う

useRefとイベントリスナーを使い、アクセシビリティのため開いたモーダル内にフォーカスを保ちます

レッスン 3/413 ステップ

「モーダルのフォーカスとキーボードトラップを扱う」はCoddyKit上の無料React Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはReact Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 React Academyコースには全4レッスンが含まれています。

ようこそ

このレッスンでは、モーダル内にフォーカストラップを実装し、キーボードユーザーがTabキーで開いているモーダルの外へ移動できないようにします。これは重要なアクセシビリティ要件です(WCAG 2.4.3)。

フォーカストラップが必要な理由

モーダルが開いたら、キーボードユーザーはモーダルのコンテンツだけを操作できる必要があります。フォーカストラップがないと、Tabキーを押したときにモーダルのオーバーレイの背後にある要素へフォーカスが移動します。それらの要素は視覚的には隠れていても、到達できてしまいます。

フォーカストラップとは

フォーカストラップはTabとShift+Tabのキーボードイベントを捕捉します。そして、フォーカスがモーダルの外へ移動しようとしたとき、モーダル内の最初または最後のフォーカス可能な要素へ戻します。

フォーカス可能な要素を見つける

すべてのインタラクティブ要素を対象にするセレクターを指定して、querySelectorAllを使用してください。無効化された要素や非表示の要素は除外します。
const FOCUSABLE = [
  'a[href]', 'button:not([disabled])',
  'input:not([disabled])', 'select', 'textarea',
  '[tabindex]:not([tabindex="-1"])'
].join(',');

const focusable = Array.from(
  modalRef.current.querySelectorAll(FOCUSABLE)
);

トラップを実装する

モーダルコンテナでkeydownイベントを監視します。Tabキーが押されたときに最後の要素へフォーカスがある場合は、最初の要素へ移動します。Shift+Tabが押されたときに最初の要素へフォーカスがある場合は、最後の要素へ移動します。
function handleTab(e) {
  const focusable = /* array of focusable elements */;
  const first = focusable[0];
  const last = focusable[focusable.length - 1];
  if (e.key !== 'Tab') return;
  if (e.shiftKey) {
    if (document.activeElement === first) {
      e.preventDefault(); last.focus();
    }
  } else {
    if (document.activeElement === last) {
      e.preventDefault(); first.focus();
    }
  }
}

開いたときにフォーカスを移動する

モーダルが開いたら、モーダル内の最初のフォーカス可能な要素へフォーカスを移動します(または`tabIndex=-1`を指定したモーダルコンテナ自体へ移動します)。これにより、モーダルを開いたボタンにフォーカスが残るのを防げます。
useEffect(() => {
  if (isOpen) {
    const first = modalRef.current?.querySelector(FOCUSABLE);
    first?.focus();
  }
}, [isOpen]);

閉じたときにフォーカスを復元する

モーダルを閉じたら、開く前にアクティブだった要素へフォーカスを戻します。モーダルが開いた時点で`document.activeElement`へのrefを保存してください。
const previousFocus = useRef(null);

useEffect(() => {
  if (isOpen) {
    previousFocus.current = document.activeElement;
  } else {
    previousFocus.current?.focus();
  }
}, [isOpen]);

focus-trap-reactライブラリを使う

本番環境では、**focus-trap-react**ライブラリの利用を検討してください。shadow DOM、iframe、動的に追加されるフォーカス可能な要素、Escapeキーによる無効化など、さまざまなエッジケースに対応しています。
import FocusTrap from 'focus-trap-react';

{isOpen && (
  <FocusTrap>
    <div role="dialog" aria-modal="true">
      {children}
    </div>
  </FocusTrap>
)}

モーダルのARIA属性

モーダルコンテナに`role="dialog"`と`aria-modal="true"`を追加してください。モーダルの見出しを指す`aria-labelledby`も追加します。これにより、スクリーンリーダーにダイアログがモーダルであることを伝え、正しく読み上げさせられます。
<div
  role="dialog"
  aria-modal="true"
  aria-labelledby="modal-title"
>
  <h2 id="modal-title">Confirm Delete</h2>
  ...
</div>

背景のinert属性

コンテナにHTMLの`inert`属性を指定すると、その中にあるすべての要素がフォーカス順序とスクリーンリーダーのアクセシビリティ対象から除外されます。堅牢に背景をブロックするには、モーダルが開いている間、`document.getElementById('root').inert = true`を設定してください。
useEffect(() => {
  const root = document.getElementById('root');
  if (isOpen) root.setAttribute('inert', '');
  else root.removeAttribute('inert');
}, [isOpen]);

確認問題

フォーカストラップされたモーダル内で、キーボードユーザーが最後のフォーカス可能な要素でTabキーを押したら、どうなるべきですか?

まとめ

フォーカストラップを実装しました。フォーカス可能な要素を見つけ、Tab/Shift+Tabを捕捉し、開いたときにフォーカスを移動し、閉じたときにフォーカスを復元します。本番環境ではfocus-trap-reactを使用し、スクリーンリーダー向けにrole='dialog'とaria-modalを追加してください。

次のレッスン

次のレッスン: **Portalを使ったツールチップとドロップダウンメニュー** — createPortalを使い、overflow-hiddenコンテナの外側にツールチップとドロップダウンをレンダリングします。
無料で開始

AI チューターと学ぶ React — 無料

ブラウザでリアルコードを書いて実行し、24/7 の AI チューターから瞬時にサポートを受け、ウェブまたはアプリで続きから学習できます。

コース
88
レッスン
324

よくある質問

「モーダルのフォーカスとキーボードトラップを扱う」レッスンは無料ですか?

はい。「モーダルのフォーカスとキーボードトラップを扱う」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、React Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 React Academyコースには全4レッスンが含まれています。

「モーダルのフォーカスとキーボードトラップを扱う」で何を学びますか?

useRefとイベントリスナーを使い、アクセシビリティのため開いたモーダル内にフォーカスを保ちます ブラウザで直接実行するハンズオンコードでReact Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

React Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのReact Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「モーダルのフォーカスとキーボードトラップを扱う」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このReact Academyレッスンでコードを書いて実行できますか?

はい。すべてのReact Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. React Portalsとは?
  2. Portalsでモーダルコンポーネントを構築する
  3. モーダルのフォーカスとキーボードトラップを扱う
  4. Portalsでツールチップとドロップダウンメニューを作る
← React Academyに戻る