モーダルやハンバーガーメニューなど、JavaScriptで制御するUIは多くのサイトで使われています。
しかし一歩間違えると、特定のユーザーにとって「操作不能な状態」を作ってしまうことがあります。
「モーダルの中から出られなくなった」
「キーボード操作だと、今どこにいるか分からない」
これらはユーザーの混乱を招き、サイトからの離脱に繋がりかねません。
そこで今回は、JavaScriptで見落としがちなフォーカス制御のポイントをまとめました。
ページ内遷移(TOPへ戻る)のフォーカス制御
「ページトップへ戻る」ボタンや、目次リンクなどページ内リンクはよく使われます。
通常のaタグであれば、ブラウザが自動的にフォーカスを移動してくれるので問題ありません。
しかし、スムーススクロール実装などの理由でJavaScriptで処理する場合は注意が必要です。
例えば下記の実装の場合、event.preventDefault()を使用することで、ブラウザ本来のフォーカスの移動を止めてしまいます。
<a href="#about">このサイトについて</a> <section id="about">・・・</section>
anchor.addEventListener('click', (e) => {
e.preventDefault(); // デフォルトの挙動を止める
const targetId = anchor.getAttribute('href');
const targetElement = document.querySelector(targetId);
window.scrollTo({
top: targetElement.offsetTop,
behavior: 'smooth'
});
});
その結果、キーボードユーザーは「移動したつもりがアンカー元にフォーカスが残ったまま」になるので、現在地が分からなくなってしまいます。
改善例
これを避けるためには、スクロールと一緒にフォーカスを移す処理を追加します。
anchor.addEventListener('click', (e) => {
e.preventDefault();
const targetId = anchor.getAttribute('href');
const targetElement = document.querySelector(targetId);
window.scrollTo({
top: targetElement.offsetTop,
behavior: 'smooth'
});
targetElement.setAttribute('tabindex', '-1'); // フォーカスを当てられるようにする
targetElement.focus({ preventScroll: true }); // focusによるジャンプを防ぐ
});
ポイントは以下の通りです。
- スクロールだけでなくフォーカスも移動させる
tabindex="-1"を付与してフォーカス可能にするpreventScroll: trueでフォーカスによる再スクロールを防ぐ
モーダルのフォーカス制御
モーダルやポップアップも、フォーカス制御において注意すべきポイントの一つです。
単純にモーダルの表示/非表示を切り替えるだけでは不十分です。
モーダルの開閉時
モーダルが開いたら、モーダル内の最初の操作可能な要素へフォーカスを移動させます。
let triggerElement = null;
openButton.addEventListener('click', () => {
triggerElement = document.activeElement; // 開く前の位置を保存
openModal();
// フォーカス可能な要素へカーソルを当てる
const firstFocusable = modal.querySelector(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
firstFocusable?.focus();
});
モーダルを閉じる時
モーダルを閉じた後に何も制御しないと、フォーカスが消えたり、ページの先頭に戻ってしまい、キーボードユーザーは迷子になってしまいます。
モーダルを開く時に、開く前の場所(モーダル表示ボタン)を変数に保持しておき、閉じるタイミングでそこへフォーカスを戻します。
closeButton.addEventListener('click', () => {
closeModal();
triggerElement?.focus(); // 元の位置へ戻す
});
これにより、モーダルを開く前の位置に戻れます。
モーダル外へのフォーカスを防ぐ
モーダル表示中に、モーダルの背景へのフォーカスが移動してしまうことがあります。
視覚的にはモーダルが前面にあっても、キーボード操作では裏側に移動してしまいます。
これを防ぐためにはinert属性を使います。
const mainContent = document.querySelector('#mainContent');
// モーダル表示中
mainContent.setAttribute('inert', '');
// モーダル非表示
mainContent.removeAttribute('inert');
inert を付与すると、その要素内にあるすべてのボタンやリンクは、
- フォーカスが当たらない
- スクリーンリーダーから除外
されます。
これによりモーダル表示中は、フォーカスがモーダル外へ漏れなくなります。
これはハンバーガーメニューやアコーディオンの表示/非表示にも応用できます。
まとめ
見た目では問題なく動いているように見えても、キーボードユーザーやスクリーンリーダー利用者にとっては「今どこにいるのかわからない」状態になっている可能性があります。
JavaScriptでUIに動きを加えるときは、「フォーカスの動きもセットで考える」ことを意識しましょう。