1. 導入:なぜFocusEventが重要なのか
現代のWebアプリケーションにおいて、キーボード操作やスクリーンリーダーによるナビゲーションは必須です。特にReactやVueなどのSPA(シングルページアプリケーション)では、画面遷移やモーダルの開閉時に「フォーカスをどこに置くか」がUXの良し悪しを決定します。FocusEventを正しく扱うことで、ユーザーの迷いを減らし、アクセシビリティ基準(WCAG)を満たす堅牢なインターフェースを実現できます。
2. 基礎知識:フォーカス関連イベントの仕組み
FocusEventには主に以下の4つの種類があります。これらを混同しないことが実装の第一歩です。
・focus: 要素にフォーカスが当たった瞬間に発生(バブリングしない)。
・blur: 要素からフォーカスが外れた瞬間に発生(バブリングしない)。
・focusin: 要素またはその子孫要素にフォーカスが当たった時に発生(バブリングする)。
・focusout: 要素またはその子孫要素からフォーカスが外れた時に発生(バブリングする)。
実務では、親コンテナで一括管理したい場合はバブリングする「focusin/focusout」を、特定の要素の状態だけを見たい場合は「focus/blur」を選択するのが定石です。
3. 実装・解決策:モーダル内フォーカス閉じ込め
モーダルを開いた際、フォーカスがモーダルの外に漏れてしまうと、スクリーンリーダー利用者は操作が困難になります。focusoutイベントを監視し、フォーカスがモーダル外へ移動しそうになったら強制的にモーダル内へ戻す仕組みが有効です。
4. サンプルプログラム:フォーカスをモーダル内に制御する例
このコードは、フォーカスがモーダルから外れそうになった場合に、強制的に先頭要素へ戻す簡易的な実装です。
// モーダル要素を取得
const modal = document.querySelector('.modal');
// focusoutイベントを監視(バブリングを利用)
modal.addEventListener('focusout', (event) => {
// フォーカスが移動した先の要素(relatedTarget)がモーダル外か判定
if (!modal.contains(event.relatedTarget)) {
console.log('フォーカスがモーダル外へ出ました。先頭に戻します。');
// モーダル内の最初のフォーカス可能要素へ強制移動
const firstFocusable = modal.querySelector('button, [href], input, select, textarea');
if (firstFocusable) {
firstFocusable.focus();
}
}
});
5. 応用・注意点:現場で陥りやすいバグの回避策
1. relatedTargetの確認: focusoutイベントで「次にどこにフォーカスが移るか」を知るには、event.relatedTargetが必須です。これがnullの場合(ブラウザ外へのクリックなど)、無理に制御を行うと予期せぬ挙動になるため、nullチェックを必ず入れましょう。
2. 要素の表示・非表示: display: none の要素に .focus() を実行してもフォーカスは当たりません。tabindex=”-1″ を付与した要素に対してプログラムからフォーカスを当てる手法が、アクセシビリティ対応の現場では一般的です。
3. ブラウザの差異: focus/blurイベントはバブリングしませんが、focusin/focusoutはバブリングします。ライブラリ開発や汎用コンポーネント作成時は、イベントの伝播特性を意識した実装を心がけてください。