そろそろ使える :focus-within 疑似クラス

Selectors Level 4 で規定されている :focus-within 疑似クラスですが、既に Firefox 52 と Safari 10.1 では使えるようになっており、 Chrome も8月リリース予定のバージョン60からデフォルトで有効になります。

これは :focus 疑似クラスが適用される要素、またはそのような要素を子孫に含む要素に対して適用されます。

これまで、

<span class="check-container">
  <input type="checkbox" id="check1"><label for="check1">チェックボックス</label>
</span>

のようなチェックボックスに対して、「チェックボックスにフォーカスが当たったとき、チェックボックスとラベルを囲う祖先要素にスタイルを適用する」ということはできませんでしたが、 :focus-within 疑似クラスを使えば

.check-container:focus-within {
  background: yellow;
}

とすることで実現可能です。

サムネイル画像
チェックボックスにフォーカスを合わせたときの様子オリジナル画像

CSSのみで作るドロップダウンメニュー

§

応用例として、ドロップダウンメニューのようなものをCSSのみで実現することが可能になります。

<style>
.nav .nav-sub {
  display: none;
}
`
.nav > li:focus-within .nav-sub {
  display: block;
}
</style>

<ul class="nav">
  <li><span tabindex="0">カテゴリ1</span>
    <div class="nav-sub">
      <ul>
        <li><a href="">カテゴリ1-1</a></li>
        <li><a href="">カテゴリ1-2</a></li>
      </ul>
    </div>
  </li>
  <li><span tabindex="0">カテゴリ2</span>
    <div class="nav-sub">
      <ul>
          <li><a href="">カテゴリ2-1</a></li>
          <li><a href="">カテゴリ2-2</a></li>
      </ul>
    </div>
  </li>
</ul>
サムネイル画像
ドロップダウンメニューの初期表示オリジナル画像
サムネイル画像
「カテゴリ1」にフォーカスを合わせたときの様子オリジナル画像

これまでも「CSSのみで作るドロップダウンメニュー」というものは存在しましたが、 :hover 疑似クラスを使ったものであり、要するにPCでのマウス操作を前提にしていたため、タッチデバイスやキーボード操作では操作不能でした。

今後は :focus-within を使うことにより、 JavaScript を使うことなくタッチデバイスやキーボード操作にも対応することが可能になります。

ドロップダウンメニューに JavaScript は不要になるのか

§

では、今後あらゆるドロップダウンメニューは JavaScript 要らずになるのかといえば、そうではありません。上記のサンプルコードは display: none で表示/非表示を切り替えていますが、 transition でアニメーションを付けるために height: 0opacity: 0 にしたいケースもあるでしょう。そうすると、たとえ見た目上は見えなくとも読み上げやキーボード操作に不都合が出てきます。

例えば、先のサンプルコードで「カテゴリ2」にフォーカスがある状態で Shift + Tab を押すと「カテゴリ1」に戻りますが、CSSを次のように変更して同じ操作をすると「カテゴリ1-2」に戻ってしまいます。

.nav .nav-sub {
  opacity: 0;
  transition: opacity 1s;
}

.nav > li:focus-within .nav-sub {
  opacity: 1;
}

また、 JavaScript を使えば WAI-ARIA の属性を設定することもできるメリットもあります。

結局のところ、動きのあるコンポーネントは今後も JavaScript を使うのが基本という点は変わらないと思います。ただし、「アニメーションは要らない、支援技術に対する考慮も最低限でよい」といったシンプルな要件の場合はCSSのみで素早く実装することができるようになりますね。

ブラウザの対応状況

§

Firefox, Safari は最新版で対応済み、 Chrome もまもなく対応される一方、 Edge は対応していません。

また、 Firefox には実装バグがあり(1361301 - [selectors4] :focus-within is lost when moving to descendant(bugzilla.mozilla.org))、先に挙げたドロップダウンメニューの例のように display: none が絡むケースでキーボード操作がうまく働きませんが、これは8月リリース予定のバージョン55で解消されるようです。

2018年10月11日追記Edgeに関しても開発優先度が "高" に変更されたので(Fix: Change priority for :focus-within pseudo-class to high · MicrosoftEdge/Status@acf7906(GitHub))、まもなく開発が始まるものと思われます。