お疲れ。今日のコードレビュー、なかなかいい線いってたけど、セレクタ周りで一つ気になるところがあったんだよね。JavaScriptで無駄にイベントリスナーをバリバリ貼り付けてDOMを操作しようとしてる箇所、見た瞬間に「あ、これCSSだけでいけるやつや」って思ったんだ。
特に、URLのハッシュ(`#`)と連動する仕組み。モーダルを開くとか、タブを切り替えるとか、アコーディオンを展開するとかさ。JSの肥大化を防ぎ、パフォーマンスを極限まで高めるための秘兵器がCSSには標準で備わってる。
今回は、そんな実務でめちゃくちゃ使えるのに、意外と忘れ去られがちで、かつ強力な`:target`疑似クラスについて徹底的に解説しようと思う。
リファレンスをただなぞるような退屈な話はしない。ブラウザの裏側の挙動から、現場で即座にコピペして使える実践的なイディオムまで、シニアの視点でガッツリ叩き込むから、しっかりついてきてくれ。
—
1. そもそも `:target` 疑似クラスとは何か?
`:target` は、一言で言うと「現在アクティブなURLのフラグメント(ハッシュID)と一致する要素」を捉えるための状態疑似クラスだ。
例えば、ブラウザのURLが `https://example.com/index.html#features` だとする。この時、HTML内に `
/ URLのハッシュが #features の時にだけスタイルが適用される /
features:target {
background-color: #fffde7;
border-color: #fbc02d;
}
非常にシンプルだが、これの本質は「JavaScriptを使わずに、URLの状態(履歴)とDOMの表示・非表示を完全に同期させられる」という点にある。SPA(シングルページアプリケーション)のルーターライブラリを入れるまでもない、小規模なウィジェットやプロトタイプ、ランディングページにおいては、これ以上ない省エネかつ堅牢なアーキテクチャになるんだ。
—
2. ブラウザの裏側で何が起きているのか?(レンダリングの仕組み)
中級からワンランク上のシニアエンジニアを目指すなら、「ブラウザが裏側でどう動いているか」を知っておく必要がある。
ユーザーがページ内リンクをクリックするか、直接URLにハッシュを入力してアクセスしたとき、ブラウザは以下のステップを踏む。
1. URLのフラグメント変更検知: ブラウザはアドレスバーの `#` 以降の文字列の変化を検知する。
2. DOMの走査とジャンプ: ブラウザはその文字列と一致する `id` 属性を持つ要素をDOMツリーから探し、画面のビューポート内へスクロール(ジャンプ)させる。
3. `:target` の状態更新: ブラウザの内部エンジン(Blink, Gecko, WebKitなど)は、直前までターゲットだった要素から `:target` フラグを外し、新しく一致した要素に `:target` フラグを付与する。
4. 再描画(Repaint / Reflow): スタイルが変更されたことによる再描画がトリガーされる。
ここで重要なのは、「ブラウザのネイティブな履歴管理(履歴スタック)と完全に連動している」という点だ。つまり、ユーザーがブラウザの「戻る」「進む」ボタンを押したとき、JavaScriptで無理やり状態を管理していなくても、`:target` の適用状態は自動的に前の状態へロールバックされる。これ、めちゃくちゃすごくないか? JSでこれをやろうとすると、`popstate` イベントを監視して、stateを復元して……と、結構なボイラープレートを書く羽目になるよね。
—
3. 実務で即戦力になる!`:target` を使った実践的コード例
理屈はこれくらいにして、実際に現場でどう使うかを見せよう。
今回は、JavaScriptを1行も書かずに実装する「ピュアCSSモーダルダイアログ」のコードだ。アクセシビリティへの配慮も忘れない、プロダクション品質のコードを書くぞ。
HTML
CSSだけで作るモーダルウィンドウ
JavaScriptのイベントリスナー?そんなものはこの世に存在しないかのように軽快に動きます。
CSS
/ ==========================================
ベースのスタイル(省略)
========================================== /
body {
font-family: sans-serif;
line-height: 1.6;
color: #333;
margin: 0;
padding: 2rem;
}
.button {
display: inline-block;
padding: 0.75rem 1.5rem;
background-color: #2563eb;
color: #fff;
text-decoration: none;
border-radius: 4px;
font-weight: bold;
transition: background-color 0.2s;
}
.button:hover {
background-color: #1d4ed8;
}
/ ==========================================
モーダルの設計 (:targetの活用)
========================================== /
/ 1. 初期状態:モーダル全体を完全に隠す /
.modal-backdrop {
position: fixed;
inset: 0;
background-color: rgba(0, 0, 0, 0.6);
display: flex;
align-items: center;
justify-content: center;
/ 非表示のための設定 /
opacity: 0;
visibility: hidden;
pointer-events: none;
transition: opacity 0.3s ease, visibility 0.3s ease;
z-index: 1000;
}
/ 2. 活性化状態:URLのハッシュが一致した時(#modal-boxになった時) /
.modal-backdrop:target {
opacity: 1;
visibility: visible;
pointer-events: auto;
}
/ モーダルコンテンツの箱 /
.modal-content {
position: relative;
background: #fff;
padding: 2rem;
border-radius: 8px;
width: 90%;
max-width: 500px;
box-shadow: 0 10px 25px rgba(0,0,0,0.2);
z-index: 2;
/ モーダルが開く時のちょっとした演出(下からスライドアップ) /
transform: translateY(20px);
transition: transform 0.3s ease;
}
.modal-backdrop:target .modal-content {
transform: translateY(0);
}
/ 閉じるボタン(テキスト) /
.modal-close {
display: inline-block;
margin-top: 1.5rem;
padding: 0.5rem 1rem;
background-color: #e5e7eb;
color: #374151;
text-decoration: none;
border-radius: 4px;
font-size: 0.875rem;
}
.modal-close:hover {
background-color: #d1d5db;
}
/ 背景をクリックして閉じるためのレイヤー /
.modal-backdrop-link {
position: absolute;
inset: 0;
z-index: 1;
}
このコードの美しいところは、アニメーション(`transition`)を含んでいるにもかかわらず、JavaScriptを1行も書いていないという点だ。`.modal-backdrop:target` になった瞬間にプロパティが切り替わり、CSSがよしなに補間してくれる。
—
4. 現場でハマりがちな「罠」とシニアからのアドバイス
`:target` は非常に強力だが、実務で使う際にはいくつか特有の「罠」が存在する。ここを理解していないと、コードレビューで容赦なく差し戻されるから注意してくれ。
罠1:画面の「ジャンプ(スクロール)」問題
前述した通り、ブラウザは `:target` を検知すると、デフォルトでその要素の位置まで強制的にスクロールしようとする。
モーダルのように「画面の中央に固定で表示させたいUI」の場合、意図しない画面スクロールが発生してUXが最悪になることがある。
【対策】
CSSの `scroll-behavior` や、要素の位置を調整するか、あるいはターゲットとなる要素を親要素のコンテナ内に配置してビューポートが大きく動かないような設計にする工夫が必要だ。また、現代のCSSであれば、そもそもモーダル開閉には `dialog` 要素やJS制御が選ばれることも多い。`:target` は「ページ内タブ」「アコーディオン」「フィルター機能」など、スクロールしても自然なUIで真価を発揮する。
罠2:URLのハッシュが残り続ける問題
モーダルを閉じるときに `href=”#”` を使うと、URLの末尾に `#` が残ってしまう。これが積み重なると、ブラウザの「戻る」ボタンを押したときに、意図せず一つ前のハッシュなしの状態に戻るまでに何度も戻るボタンを押すハメになることがある。
【対策】
HTML5の History API が使える環境であれば、JavaScriptでクリーンアップする選択肢も頭に入れておくこと。ただ、純粋なプロトタイピングや、SEO的にハッシュが残っても問題ないLP(ランディングページ)のセクション切り替えなどでは、この特性はむしろ「ブラウザバックが効いて便利」というメリットに化ける。
—
5. まとめ
CSSの `:target` 疑似クラスは、DOMの状態管理をブラウザのURL機構にオフロードできる、非常にエレガントな機能だ。
- JS不要: イベントリスナーの管理やメモリリークの心配がゼロ。
- ネイティブ連携: ブラウザの「戻る」「進む」ボタンと完全に同期する。
- 宣言的記述: 「どのURLのときに、どうあるべきか」をCSSだけで完結できる。
すべての複雑なUIをCSSだけで解決しようとするのはナンセンスだが、「JSを使うまでもないシンプルなトグル状態」において、`:target` は君の武器庫の中で最も切れ味の鋭いナイフになるはずだ。
次の機能開発で「これ、JS書かずに `:target` でいけそうだな」って閃いたら、ぜひシレっと導入してみてほしい。チームメンバーから「お、分かってるね」って一目置かれること請け合いだよ。
さて、コーヒーブレイクにしようか。何か質問はあるかい?

コメント