はじめに: CSSがイベントを処理する時代
JavaScriptのイベントリスナーを使わずにUIの状態変化へ反応したい場合、CSS擬似クラスがその解決策になり得ます。最近のCSSは、ますます多くの状態を追跡し、JavaScriptイベントに似た動作を提供しています。この記事では、実際によく使われる擬似クラスをJavaScriptイベントと対比しながら解説し、近い将来導入されるevent-trigger機能も紹介します。
こちらの記事は元記事(根拠資料)を基に、日本の開発者向けに再構成しました。
本編1: 状態ベースの擬似クラスとJavaScriptイベントの比較
:hover, :active – 基本中の基本
:hoverは、マウスポインタが要素に出入りする間の状態を表します。JavaScriptのpointerenter/pointerleaveイベントに相当します。:activeは、マウス・タッチ・スタイラスで要素を押している間にマッチし、pointerdownとpointerup/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イベント |
|---|---|
:buffering | waiting |
:muted | volumechange |
:paused | pause |
:playing | playing |
:seeking | seeking |
:stalled | stalled |
:volume-locked | N/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アニメーショントリガーの仕様変更を注視し、ブラウザ互換性を確認してみてください。実際のプロジェクトに適用する前に、小さなコンポーネントからテストすることをお勧めします。
合わせて読みたい記事:
