JavaScript 逆引き集 | フォーカス制御

JavaScript JavaScript
スポンサーリンク

フォーカス制御の基本 — el.focus()

フォームやボタンに「今ここを操作できます」と意識を集めるのがフォーカスです。el.focus() は要素にプログラムからフォーカスを当てる最小の一手。入力欄のカーソル移動、モーダルを開いた直後の初期フォーカスなどに使います。


基本の使い方

<input id="name" placeholder="お名前">
<button id="go">フォーカス</button>
<script>
  const input = document.getElementById("name");
  document.getElementById("go").addEventListener("click", () => {
    input.focus(); // 入力欄にカーソルが移動
  });
</script>
HTML
  • 役割: その要素にキーボード入力やショートカットを受け付ける状態を作る。
  • 対象: ふつうはフォーム要素(input、textarea、select)、ボタン、リンクなどがフォーカス可能。

よく使うテンプレート集

ラベル: モーダルを開いたら最初の入力にフォーカス

function openModal() {
  const modal = document.getElementById("modal");
  modal.classList.add("show");
  const firstField = modal.querySelector("input, button, [tabindex]:not([tabindex='-1'])");
  firstField?.focus({ preventScroll: true }); // スクロールさせずにフォーカス
}
JavaScript

ラベル: エラー箇所にジャンプ

function showError(el, msg) {
  const hint = el.nextElementSibling;
  hint.textContent = msg;
  el.focus(); // すぐ修正しやすい
}
JavaScript

ラベル: 入力欄を作った直後にフォーカス(タイミング調整)

const list = document.getElementById("list");
function addEditableItem() {
  const li = document.createElement("li");
  li.innerHTML = `<input type="text" class="edit">`;
  list.appendChild(li);
  requestAnimationFrame(() => {
    li.querySelector(".edit").focus(); // DOMに追加されたフレーム後に確実にフォーカス
  });
}
JavaScript

ラベル: キャレット(カーソル)を末尾に置く

function focusEnd(input) {
  input.focus();
  const v = input.value;
  input.setSelectionRange(v.length, v.length);
}
JavaScript

フォーカス可能にする設定(tabindex)

<div id="card" tabindex="0">カード(フォーカス可能)</div>
<script>
  const card = document.getElementById("card");
  card.addEventListener("focus", () => card.classList.add("focused"));
  card.addEventListener("blur", () => card.classList.remove("focused"));
</script>
HTML
  • tabindex=0: その要素を「標準のタブ移動順」に含める。
  • tabindex>0: 独自の順序を強制できるが複雑化しやすいので避けるのが無難。
  • tabindex=-1: フォーカスはできる(el.focus()可)が、Tabキーでは移動しない。モーダルの閉じるボタンなどに便利。

イベントとフォーカスの流れ

  • focus / blur: 対象要素で発火。バブリングしない。
  • focusin / focusout: 親にも届く(バブリングする)。フォーム全体で一括管理に便利。
const form = document.querySelector("form");
form.addEventListener("focusin", (e) => {
  e.target.classList.add("has-focus");
});
form.addEventListener("focusout", (e) => {
  e.target.classList.remove("has-focus");
});
JavaScript
  • ラベル: 1箇所のハンドラでフォーム内の全フィールドの入/抜けを監視できる。

実務でのコツ

  • 表示状態との整合: 非表示(display: none)や disabled の要素にはフォーカスできない。表示してからフォーカスする。
  • preventScroll を活用: el.focus({ preventScroll: true }) で意図せぬスクロールを防ぐ。
  • タイミング: DOM追加直後はフォーカスが効かないことがある。requestAnimationFramesetTimeout(0) で次のタイミングにずらす。
  • アウトラインは残す: CSSで outline: none を安易に消すとキーボードユーザーが迷う。代替のフォーカススタイルを必ず用意する。

ありがちなハマりポイントと対策

  • フォーカスできない(disabled/hidden):
    • 対策: 有効化して表示してから focus()visibility: hidden はフォーカス可能だが見えないので注意。
  • スクロールが勝手に動く:
    • 対策: focus({ preventScroll: true }) を使う。
  • Tab移動の順番が混乱:
    • 対策: tabindex > 0 は原則使わず、DOM順に沿った設計に。
  • モーダルの外へフォーカスが逃げる:
    • 対策: 初期フォーカス+フォーカストラップ(Tabでモーダル内を循環させる)。必要ならキーハンドリングで対応。

練習問題(手を動かして覚える)

<div id="app">
  <button id="open">モーダルを開く</button>
  <div id="modal" class="modal" aria-hidden="true">
    <input id="email" type="email" placeholder="Email">
    <button id="submit">送信</button>
    <button id="close">閉じる</button>
  </div>
</div>

<style>
  .modal { display: none; gap: 8px; }
  .modal.show { display: flex; }
  :focus { outline: 2px solid #4c9aff; }
</style>

<script>
  const modal = document.getElementById("modal");
  const openBtn = document.getElementById("open");
  const closeBtn = document.getElementById("close");
  const email = document.getElementById("email");

  openBtn.addEventListener("click", () => {
    modal.classList.add("show");
    modal.setAttribute("aria-hidden", "false");
    // スクロールさせずに初期フォーカス
    email.focus({ preventScroll: true });
  });

  closeBtn.addEventListener("click", () => {
    modal.classList.remove("show");
    modal.setAttribute("aria-hidden", "true");
    openBtn.focus(); // 元のトリガーへ戻す
  });

  // Enterで送信ボタンへ移動してみる
  email.addEventListener("keydown", (e) => {
    if (e.key === "Enter") document.getElementById("submit").focus();
  });
</script>
HTML

直感的な指針

  • 「見える・有効」な要素に、適切なタイミングで el.focus() を当てる。
  • スクロール抑制やフォーカス順は小さく整える。tabindex=0/-1 を理解して最小限で。
  • 見た目のアウトラインは残して、キーボード操作の手触りを大事にする。
タイトルとURLをコピーしました