はじめに: CSSがイベントを処理する時代

JavaScriptのイベントリスナーを使わずにUIの状態変化へ反応したい場合、CSS擬似クラスがその解決策になり得ます。最近のCSSは、ますます多くの状態を追跡し、JavaScriptイベントに似た動作を提供しています。この記事では、実際によく使われる擬似クラスをJavaScriptイベントと対比しながら解説し、近い将来導入されるevent-trigger機能も紹介します。

こちらの記事は元記事(根拠資料)を基に、日本の開発者向けに再構成しました。

本編1: 状態ベースの擬似クラスとJavaScriptイベントの比較

:hover, :active – 基本中の基本

:hoverは、マウスポインタが要素に出入りする間の状態を表します。JavaScriptのpointerenter/pointerleaveイベントに相当します。:activeは、マウス・タッチ・スタイラスで要素を押している間にマッチし、pointerdownpointerup/pointercancelの間の状態と同じです。

:focus:focus-visible

:focusはJavaScriptのfocusイベントに対応し、:focus-visibleはキーボード操作かどうかなどのブラウザのヒューリスティックに基づいて、フォーカスインジケータが必要な場合にのみマッチします。JavaScriptでこの状態を確認したい場合は、matches()を使用できます。

element.addEventListener('focus', (event) => {
  if (event.target.matches(':focus-visible')) {
    // キーボードフォーカス表示のためのスタイル適用
  }
});

:focus-within:has() – 親要素で子要素の状態を検出

これらの擬似クラスを使用すると、子要素の状態を親要素で検出できます。フォーム全体にフォーカスが当たったときにスタイルを適用したい場合、以下の2つのセレクタは同じ動作をします。

form:focus-within {
  /* 内部要素にフォーカスがあるときフォームを強調 */
}

form:has(:focus) {
  /* 上記と同じ効果 */
}

:checkedとフォーム検証擬似クラス

:checkedはチェックボックスやラジオボタンの選択状態を表し、JavaScriptのchangeイベントに似ています。フォーム検証関連では、:valid, :invalid, :user-valid, :user-invalidがあります。特に:user-validは、ユーザーが値を入力してフォーカスを外した後にのみマッチするため、よりユーザーフレンドリーなUXを提供します。

input.addEventListener('input', () => {
  if (input.validity.valid) {
    // 有効な入力
  } else {
    // 無効な入力 (invalidイベントは発生しない)
  }
});

メディア要素擬似クラス – 動画・音声の状態スタイリング

インタラクティブメディア要素の再生状態をCSSでスタイリングできる新しい擬似クラスが登場しました。Chromeでは未サポートですが、Firefoxではサポートされ、Interop 2026に含まれています。

擬似クラスJavaScriptイベント
:bufferingwaiting
:mutedvolumechange
:pausedpause
:playingplaying
:seekingseeking
:stalledstalled
:volume-lockedN/A
video:playing {
  /* 再生中のボーダー強調 */
  outline: 2px solid green;
}

:popover-open, :open, :modal など

ポップオーバー、ダイアログ、details要素の開閉状態を表す擬似クラスもあります。JavaScriptではtoggleイベントを受信してopenプロパティを確認する必要がありますが、CSSでは直接状態を把握できます。

本編2: event-trigger – CSSでイベントをリッスンする未来機能

概念と基本構文

Chromeのスクロール駆動アニメーション実装で登場したevent-triggerは、まだどのブラウザでもサポートされていない実験的な機能です。しかし可能性を先取りして見ると、CSSで実際のイベントをリッスンし、アニメーションをトリガーできるようになります。

@keyframes fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

button {
  /* クリックで --event トリガー発生 */
  event-trigger: --event click;
}

div {
  /* --event 発生時にアニメーション再生 */
  animation-trigger: --event play-forwards;
  animation: fade-in 300ms both;
}

ステートフルなイベントトリガー

興味(interest)の開始/終了のような状態ベースのイベントは、スラッシュ(/)で区切り、各状態に応じたアニメーションアクションを指定します。

@keyframes fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

button {
  /* 興味の開始 / 終了 */
  event-trigger: --event interest / interest;
}

div {
  /* 開始時は順方向、終了時は逆方向に再生 */
  animation-trigger: --event play-forwards play-backwards;
  animation: fade-in 300ms both;
}

利用可能なアニメーションアクションは、none, play, play-once, play-forwards, play-backwards, pause, reset, replay などです。

注意点と制限

  • 未サポート機能のため、プロダクションで使用せず、ブラウザのサポート状況を必ず確認してください。
  • event-triggerがすべてのイベントを置き換えるわけではありません。特に複雑な状態ロジックはJavaScriptが必要です。
  • CSS擬似クラスは状態を表すものであり、イベントではないため、イベントの発生順序に依存するロジックはJavaScriptで実装するのが安全です。

まとめ: 実務適用のアドバイス

CSS擬似クラスを上手く活用すれば、JavaScriptコードを減らし、パフォーマンスを向上させることができます。特にフォーム検証、フォーカス管理、メディア要素の状態表示などで強力なツールになります。ただし、実験的な機能は慎重に扱い、ポリフィルや代替手段を検討してください。

次のステップとしては、CSSアニメーショントリガーの仕様変更を注視し、ブラウザ互換性を確認してみてください。実際のプロジェクトに適用する前に、小さなコンポーネントからテストすることをお勧めします。


合わせて読みたい記事:

CSS pseudo-classes and JavaScript events comparison diagram with browser examples Dev Environment Setup

本コンテンツは、信頼性の高い情報源をもとにAIツールを活用して作成され、編集者によるレビューを経て公開されています。専門家によるアドバイスの代替となるものではありません。