:focus-visible と JavaScript

自己紹介

ますきー (@mascii_k)

(株)ビザスク
フロントエンドエンジニア

:focus-visible とは?


  • キー操作時のみスタイルを当てるための疑似クラス


  • Chrome では 2020/10 から使えるようになった

https://caniuse.com/css-focus-visible

:focus-visible のデモ






JS でもキー操作中であるかわかるか?


document.querySelector()
:focus-visible な要素を取得することができる



以下のようにしてキー操作中であるかがわかる:
const hasFocusVisible = () => {
  try {
    return !!document.querySelector(':focus-visible');
  } catch {
    // :focus-visible 非対応ブラウザ
    return false;
  }
};

ユースケース

  • をクリックしたら、次に入力すべき入力欄にフォーカスが移る
  • キー操作で を選択するときは、入力欄にフォーカスが移らない

ビザスクlite 公募案件作成画面

:focus-visible と WebComponents


Shadow DOM によって :focus-visible な要素が隠蔽される

例: Wired Elements
ReactVueAngularSvelte
  • Shadow DOM 内部の要素は document.querySelector() ではアクセスできない
  • つまり、document.querySelector(':focus-visible') が通用しない

WebComponents もキー操作中であるかわかるか?


Shadow Root のモードが closed ではなく open であれば、Shadow Root の
内部にアクセスすることで :focus-visible な要素を取得することができる



以下のようにしてキー操作中であるかがわかる:
const hasFocusVisible = (): boolean => {
  try {
    // フォーカス中の要素またはコンポーネントを取得
    let activeElement: Element | null = document.activeElement;

    while (activeElement) {
      // フォーカス中の要素が :focus-visible なものであるか判定
      if (activeElement.matches(':focus-visible')) {
        return true;
      } else if (!activeElement.shadowRoot) {
        return false;
      }
      activeElement = activeElement.shadowRoot.activeElement;
    }

    return false;
  } catch {
    return false;
  }
};
  • document.activeElement でフォーカス中の要素またはコンポーネントを取得できる
  • Element#querySelector は使うことがあったが Element#matches は初めて使った
  • この実装をして <iframe> 内部に :focus-visible な要素があるケースに気付いた

まとめ


  • :focus-visible を JS からも活用できた
  • Shadow DOM が絡むとフォーカス周りは難しい