【実務・中級編】 not関数型疑似クラス – CSS実践ガイド

やあ。今日も今日とてCSSの海を泳ぎ回っているかい?
デザインカンプを忠実に再現するだけなら初級者でもできる。だが、保守性が高く、将来の仕様変更にもビクともしない堅牢なスタイルシートを組み上げるとなると話は別だ。特に「例外処理」の書き方一つで、そのコードがプロの仕事か、単なる応急処置の寄せ集めかが一発でバレる。

今回は、実務でCSSを書くなら避けて通れない、いや、むしろ積極的に使いこなしてほしい`:not()`疑似クラス(否定疑似クラス)について深掘りしよう。

「ああ、指定したセレクタ以外の要素を選ぶやつね、知ってるよ」と思ったそこの君。
少し待ってほしい。君が普段使っている`:not()`は、本当にそのポテンシャルを最大限に引き出せているかい?実は仕様の進化やブラウザの裏側の仕組みを知ることで、`:not()`はただの「除外フィルター」から、CSSアーキテクチャの強力な武器へと化けるんだ。

現場のリアルな知見を交えて、徹底的に解説していこうか。

—

1. `:not()`疑似クラスの標準仕様と、ブラウザが裏側でやっていること

まずは基本のおさらいと、一歩進んだ仕様の話だ。
`:not()`は、引数に渡した「セレクタに一致しない」要素をターゲットにする。

/ 「.is-active」が付いていないボタンをすべて選択 /
button:not(.is-active) {
opacity: 0.6;
}

これ自体は基本中の基本だ。だが、ここからがプロの視点だ。仕様の変遷において、`:not()`は非常に大きなアップデートを遂げていることを知っているかい?

近代CSSにおける「複セレクタ」と「詳細度」の罠

昔のCSS(Level 3の時代)の`:not()`には、大きな足かせがあった。それは「引数に取れるのは単一のセレクタ(Simple Selector)のみ」というルールだ。つまり、`:not(.class1, .class2)`のようなカンマ区切りでの複数指定や、複合セレクタが直感的に書けなかったり、ブラウザによって挙動が怪しかったりした。

しかし、Selectors Level 4以降、近代ブラウザ(Safariも含めて完全に普及済みだ)では、`:not()`のなかに複数のセレクタをカンマ区切りで渡せる(リスト化できる)ようになった。

/ モダンな書き方:複数の条件をまとめて除外する /
:not(.is-active, .is-disabled, [hidden]) {
cursor: pointer;
}

そして、ここで一番重要な詳細度(Specificity)の話をしよう。
ブラウザの裏側のエンジン(BlinkやGeckoなど)がCSSを解析する際、`:not()`自体の詳細度は0だが、括弧の中に書いたセレクタの中で「最も強い詳細度」がそのまま全体の詳細度になるという仕様がある。

/ この場合、IDセレクタ(#submit)が含まれているため、全体の詳細度が跳ね上がる /
div:not(#submit) {
background: #f0f0f0;
}

「あれ、なんでこのスタイルの適用が上書きされちゃんだ?」と悩んだときは、大抵この`:not()`の中身が原因だったりする。裏側の仕組みを理解していないと、無駄に詳細度を爆発させてしまい、後々のメンテナンスで痛い目を見る典型的なパターンだ。心当たりがないかい?

—

2. 現場で即戦力になる!実践的なユースケースと綺麗めコード

理屈はこのあたりにして、実際のフロントエンド開発でどう使うべきか、具体的なシチュエーションを見ていこう。コピペしてすぐにでもプロジェクトに組み込めるコードを用意した。

ユースケースA:リストの「最後の要素以外」にボーダーを引く(いわゆる “Lobotomized Owl” の進化系)

リストの区切り線(border-bottom)を入れるとき、最後の要素の下線だけは消したい。よくある要件だよね。
昔は `:last-child` で打ち消すのが定石だったが、`:not()`を使えばもっとスマートに書ける。

  • ニュース記事 01
  • ニュース記事 02
  • ニュース記事 03(ここにはボーダーを出したくない)

.item-list li {
padding: 1rem 0;
/ 最後の要素「以外」に下線を引きまくる /
border-bottom: 1px solid var(–color-border, #eee);
}

/ 否定疑似クラスで最後の要素を除外する美しさ /
.item-list li:not(:last-child) {
/ ここに余計な打ち消し(border: none等)を書く必要が一切ない /
}

……いや、待て待て。これだと「除外するだけ」になってしまうから、正確にはこうだ。

/ 最初から「最後の要素以外」をターゲットにしてボーダーを付与する /
.item-list li:not(:last-child) {
border-bottom: 1px solid #e0e0e0;
}

これの何が素晴らしいかって、「打ち消しのコード(override)」を一切書かなくていいという点だ。CSS設計において、一度書いたスタイルを別のセレクタで打ち消す行為は、コードの複雑性を増す悪手になりやすい。`:not()`を使うことで、「最初から適用しない」というクリーンなロジックが組める。

ユースケースB:フォームのバリデーションにおける「入力中かつエラーではない」状態のスタイリング

複雑なフォーム画面を作るとき、ユーザーが入力している最中のインタラクティブなフィードバックはUXの命だ。ここで`:not()`と他の疑似クラスを組み合わせる真価が発揮される。

/
「プレースホルダーが表示されている状態(未入力)」でもなく、
「無効な状態(:invalid)」でもない、かつ、ユーザーが一度でも触った(またはフォーカスが当たっている等)の要素
……をスマートにスタイリングする。
/

.signup-form input:not(:placeholder-shown):not(:invalid) {
border-color: var(–color-success, #2e7d32);
background-image: url(“data:image/svg+xml,…”); / 成功アイコンなど /
}

.signup-form input:not(:placeholder-shown):invalid {
border-color: var(–color-error, #c62828);
}

`:placeholder-shown` と `:not()` をチェイン(連続して記述)させることで、「ユーザーが何かしらの文字を入力した瞬間」をエレガントに捉えることができる。JavaScriptでわざわざ `input` イベントを監視してクラスを付け外しする? いやいや、モダンなCSSならこれだけで完結するケースがたくさんあるんだ。

—

3. シニアが教える、`:not()`を使う際のアンチパターンと注意点

ここまで`:not()`の素晴らしさを語ってきたが、諸刃の剣でもある。間違った使い方をすると、コードが読めないブラックボックスになってしまう。チーム開発で嫌われるアンチパターンを共有しておこう。

アンチパターン1:深すぎるネストと可読性の崩壊

/ 読むだけで頭痛がしてくる悪い例 /
section:not(.is-archived) > div:not(.sidebar) ul li:not(.is-hidden) a:not([target=”_blank”]) {
color: blue;
}

おいおい、冗談はやめてくれと言いたくなるコードだ。
`:not()`の中にさらに別の条件を何重にも重ねたり、長いセレクタチェーンの一部に組み込んだりすると、「一体どの要素が選ばれているのか」を脳内パースするだけで数分かかるゴミコードが完成する。
CSSは「誰が読んでも一瞬で意図が伝わる」ことが正義だ。もし条件が複雑になりすぎるなら、BEMなどの命名規則を頼るか、HTML側に適切なクラス(例: `.is-target` など)を付与することを検討してほしい。道具に頼りすぎてHTMLの構造を無視してはいけないよ。

アンチパターン2:パフォーマンスへの過度な心配(と、現実)

「`:not()`を多用すると描画パフォーマンスが落ちるのでは?」と心配する真面目な後輩がいる。
結論から言うと、現代のブラウザのセレクタマッチングエンジン(Selectors API)は極めて高度に最適化されているため、常識的な範囲(数千要素レベル未満)で`:not()`を使ったからといって、体感できるほどのパフォーマンス低下が起きることはまずない。

ただし、巨大なDOMツリーのルート(`body` や “ の直下など)で `:not()` のような全称セレクタと組み合わせた超広範囲な否定を行えば、当然ブラウザのペイント・レイアウトコストには悪影響を与える。常識的なスコープ(特定のコンポーネント内)で使う分には、パフォーマンスよりも「可読性の維持」を優先して問題ない。

—

まとめ:例外を美しく扱うために

CSSのコーディングは、往々にして「例外処理の連続」だ。
「基本はこうだけど、ここだけは除外したい」「この状態以外の時にスタイルをあてたい」――そんな現場の泥臭い要求に対して、`:not()`疑似クラスは最高にスマートな解答を用意してくれている。

無駄なJavaScriptのイベント監視を減らし、CSSの記述量を削ぎ落とし、スタイルの詳細度をコントロールする。
今日から君の書くコードの「例外処理」を、`:not()`を使ってワンランク上の洗練されたものにアップデートしてくれ。期待しているよ。

コメント

タイトルとURLをコピーしました