CSSで特定の要素にスタイルを適用したくない場面は、ナビゲーションメニューの端など、レイアウト調整の過程で頻繁に発生します。除外条件を指定する際に、よく使われるのが「not」疑似クラスで、特定のクラス名などを対象外にする否定疑似クラスです。
この記事では、CSSのnotで複数の条件を除外する基本的な使い方を解説していきます。クラス名の連結による指定方法など、サンプルコード付きで解説していきますので、ぜひ参考にしてください。
CSSのnotで複数の条件を除外する方法
CSSのnotで複数の条件を除外するには、:not()を連結する書き方と、カンマ区切りで複数の条件を渡す書き方の2種類があります。以下の2つの方法について、ブラウザ対応状況も踏まえた選択が基本です。
複数のクラスを連結して指定する
特定の要素から複数の条件を同時に除外したい場合は、否定疑似クラスであるnotを続けて記述します。複数のnotを直列に繋ぐことによって、指定した条件をすべて満たさない要素にのみスタイルを適用できるという仕組みです。
公式ドキュメントでは、複数のセレクターを指定する方法について、次のように説明しています。
複数のセレクターを同時に否定することができます。例えば、 :not(.foo, .bar) は :not(.foo):not(.bar) と同じです。
出典:MDN Web Docs 複数セレクターを同時に否定する際の挙動と等価な書き方
このように記述することによって、複数のクラスを確実に除外できます。
複数のクラスを連結して指定する使い方は、以下の通りです。
li:not(.first):not(.last) {
margin-bottom: 10px;
}
上記のコードでは、リスト要素のうちfirstクラスとlastクラスを持たない要素に対してのみ、下方向のマージンを指定しています。複数の条件を確実に弾くことができる、実務でも頻出の書き方です。
一度全体に適用したスタイルを後から上書きするよりも、最初からnotで除外した方がコードをシンプルに保てます。保守性の高いCSSを記述するうえで、上書きコードを減らす意識は役立つ習慣です。
ブラウザ互換性に応じた書き方を選択する
:not()の引数にカンマ区切りで複数の条件を指定する構文は、CSS Selectors Level 4(W3C Working Draft)で定義されたセレクターリスト引数として、Chrome 88 以降・Firefox 84 以降・Safari 9 以降・Edge 88 以降を含む主要モダンブラウザで広くサポートされています。Can I use のデータ(記事執筆時点)によると、グローバルサポート率は96%超です。
IE 全バージョンや古いブラウザをサポートする必要がある場合のみ、カンマ区切りではなく連結形式を選択することを推奨します。以下は両方の書き方の比較です。
/* カンマ区切り: 主要モダンブラウザで有効(Selectors Level 4 対応) */
li:not(.first, .last) {
margin-bottom: 10px;
}
/* 連結形式: IE など古いブラウザ互換が必要な場合に使用 */
li:not(.first):not(.last) {
margin-bottom: 10px;
}
上記の2つの記述は同じ結果をもたらします。IE サポートは2022年6月に終了しており、現代のプロジェクトではカンマ区切りをそのまま使用して問題ありません。
古いブラウザへの対応が必要な場合のみ、連結形式を選択するのが適切な判断です。どちらの書き方を選ぶかは、対応すべきブラウザの要件に応じて判断してください。
CSSのnotの複数指定を活用した実用例
MDN Web Docsでも解説されている通り、CSSのnotを連結して複数の条件を指定するテクニックは、実際の開発現場でも役立ちます。前述の通り、打ち消しのコードを減らせるため、メンテナンスしやすいCSSを記述するのに効果的です。
リストの両端の要素を除外する
箇条書きのリストに対して、最初と最後の要素以外にスタイルを適用したい場面は少なくありません。notに疑似クラスを複数組み合わせることによって、中間の要素にのみ指定を反映できます。
リストの両端を除外するコードの書き方は、以下の通りです。
li:not(:first-of-type):not(:last-of-type) {
border-bottom: 1px solid #ccc;
}
上記のコードでは、最初の要素と最後の要素のどちらにも該当しないリスト項目にのみ、下線を設定しています。これにより、リストの末尾に余分な装飾が追加されるのを防ぎます。
従来のように全体へスタイルを適用してから最初と最後だけ元に戻す処理が不要になるのが特徴です。打ち消しの記述を減らせるため、コード全体の可読性向上に繋がります。
階層を跨いで特定の要素を除外する
親要素と子要素の階層が複雑な場合でも、notの複数指定は威力を発揮します。これは複数の否定条件を親子で併用する例であり、特定の親要素配下にある、特定のクラスを持たない子要素に対してのみスタイルを当てるといった制御が可能です。
階層を跨いだ除外を実装するコードは、以下の通りです。
.parent-box:not(.is-active) .child-item:not(.is-hidden) {
opacity: 0.5;
}
上記のコードでは、.parent-box:not(.is-active)が親の否定条件、.child-item:not(.is-hidden)が子の否定条件を表しており、両方の条件を満たした時だけスタイルが反映される二重否定の仕組みを実現しています。親の属性と子の属性を別々に評価できるのが特徴です。
この手法を活用すれば、HTMLの構造を変えずに、CSSだけで柔軟な表示の切り替えを実現できます。状態管理用のクラスを付与する手間を最小限に抑えながら、複雑な除外条件も記述可能です。
CSSのnotを複数指定する際の注意点
CSSのnotを使って複数の条件を指定する際には、いくつかの制限事項が存在します。これらの制限を把握しておかないと、意図した通りにスタイルが適用されない原因となるため、仕様を正しく理解しておくことが必要です。
具体的な注意点は、以下の通りです。
- 疑似要素を内部に記述しない
- 別のnotを入れ子にしない
それぞれの制約について、正しくない書き方と正しい記述方法を合わせて解説します。事前に把握しておくことで、意図しないスタイルの適用漏れを防げます。
疑似要素を内部に記述しない
否定疑似クラスの引数として、::beforeや::afterのような疑似要素を含めることは仕様上認められていません。また、疑似要素はセレクターチェーンの末尾に位置しなければならないため、div::before:not(.active)のように疑似要素の後ろに疑似クラスをチェーンする書き方も無効です。
誤った指定方法と正しい代替アプローチを、以下のコードで確認できます。
/* NG: notの内部に疑似要素を指定しているため無効 */
div:not(::before) {
color: red;
}
/* NG: 疑似要素の後ろに:not()をチェーンする書き方も無効 */
div::before:not(.active) {
content: "NEW";
color: red;
}
/* OK: 親要素側に:not()を付与して疑似要素のスタイルを制御する */
div:not(.active)::before {
content: "NEW";
color: red;
}
疑似要素のスタイルを出し分けたい場合は、上記の OK 例のように、親要素や対象要素にクラスを付与して制御する方法が正しいアプローチです。:not()の引数に含められるのは、単純セレクターや複合セレクター、あるいは複雑セレクター(Selectors Level 4)ですが、疑似要素は含めることができない仕様です。
仕様の詳細は MDN Web Docs の :not() リファレンスで確認できます。実装前に最新の仕様を参照しておくと、環境差異による予期しない動作を防げます。
別のnotを入れ子にしない
CSS Selectors Level 4 では、:not()の引数に複合セレクターを記述することは構文として許容された仕様です。しかし、:not()の中にさらに:not()を入れ子にすることは、仕様において、:not() の引数に使用できるセレクターから疑似クラス(:not()自身を含む)が除外されているため、意図した動作とならない可能性があります。
p:not(:not(.special))のような二重否定は、実質的に p:is(.special) と等価な挙動を示す場合もあり、ブラウザ依存の動作となりやすいため避けることを推奨します。
入れ子による問題のある記述と、正しい記述方法は以下の通りです。
/* 非推奨: notの中にnotを入れ子にする(ブラウザ依存の動作となりうる) */
p:not(:not(.special)) {
font-weight: bold;
}
/* OK: 別のnotを入れ子にせず、個別に連結させる */
p:not(.special):not(.disabled) {
font-weight: bold;
}
上記のコードでは、前半にネストを用いた非推奨の構文を示した例です。複数の条件を除外したいときは、後半のように:not()を連続して並べることで、AND条件として適切に処理されます。
可読性と予測可能な動作を確保するために、MDN Web Docsなどの公式リファレンスも確認し、フラットな連結構成で条件を書くことを推奨します。
CSSのnotの複数指定に関するよくある質問
全ての要素を除外する全称セレクタは使えますか?
全称セレクタ(*)を:not()の引数として指定することは無意味であり、実務では使われない書き方です。:not(*)のように記述した場合、セレクタとしては解析されますが、全ての要素を除外対象とするためマッチする要素が0件となり、スタイルが一切適用されません。
たとえば、:not(*)のように記述した場合、ブラウザ上で構文エラーを引き起こすわけではありませんが、いかなる要素にもマッチしないため、スタイルは一切適用されません。特定の要素だけを除外したい場合は、クラスやタグ名などを明示的に指定する設計が基本です。
複数の:not()を連結するとスペシフィシティはどう変わりますか?
:not()は引数のセレクターのスペシフィシティ(詳細度・スタイルの優先度を決める仕様値)を引き継ぐ性質があります。そのため、:not(.foo):not(.bar)のように複数の:not()を連結すると、各引数のスペシフィシティが加算される点を把握しておくと役立ちます。
スペシフィシティの加算により、予期しないスタイルの競合が発生しやすくなるため、:not()の連結数が増えるほど、他のルールとの優先度関係を意識した設計が必要です。
後から打ち消しのCSSを書くのとどちらが良いですか?
最初から:not()を使用して除外条件を定義するアプローチが推奨されます。一度全体に適用したスタイルを別のクラスで上書きして打ち消す記述は、コードが冗長になる原因です。
たとえば、最後のリスト要素だけ下線を消したい場合に、後からborder: none;を追加するよりも、最初から:not(:last-child)で除外したほうがシンプルに実装できます。打ち消しのコードを減らすことで、保守性の高いクリーンなCSSを維持できるのが大きなメリットです。
※上記コンテンツの内容やソースコードはAIで確認・デバッグしておりますが、間違いやエラー、脆弱性などがある場合は、コメントよりご報告いただけますと幸いです。
