:not()
The :not()
CSS pseudo-class represents elements that do not match a list of selectors. Since it prevents specific items from being selected, it is known as the negation pseudo-class.
Try it
The :not()
pseudo-class has a number of quirks, tricks, and unexpected results that you should be aware of before using it.
Syntax
The :not()
pseudo-class requires a selector list, a comma-separated list of one or more selectors, as its argument. The list must not contain a pseudo-element, but any other simple, compound, and complex selectors are allowed.
:not(<complex-selector-list>) {
/* ... */
}
Description
There are several unusual effects and outcomes when using :not()
that you should keep in mind when using it:
- Useless selectors can be written using this pseudo-class. For example,
:not(*)
matches any element which is not an element, which is obviously nonsense, so the accompanying rule will never be applied. - This pseudo-class can increase the specificity of a rule. For example,
#foo:not(#bar)
will match the same element as the simpler#foo
, but has the higher specificity of twoid
selectors. - The specificity of the
:not()
pseudo-class is replaced by the specificity of the most specific selector in its comma-separated argument of selectors; providing the same specificity as if it had been written:not(:is(argument))
. :not(.foo)
will match anything that isn't.foo
, including<html>
and<body>
.- This selector will match everything that is "not an X". This may be surprising when used with descendant combinators, since there are multiple paths to select a target element. For instance,
body :not(table) a
will still apply to links inside a<table>
, since<tr>
,<tbody>
,<th>
,<td>
,<caption>
, etc. can all match the:not(table)
part of the selector. To avoid this, you can usebody a:not(table a)
instead, which will only apply to links that are not descendants of a table. - You can negate several selectors at the same time. Example:
:not(.foo, .bar)
is equivalent to:not(.foo):not(.bar)
. - If any selector passed to the
:not()
pseudo-class is invalid or not supported by the browser, the whole rule will be invalidated. The effective way to overcome this behavior is to use:is()
pseudo-class, which accepts a forgiving selector list. For example:not(.foo, :invalid-pseudo-class)
will invalidate a whole rule, but:not(:is(.foo, :invalid-pseudo-class))
will match any (including<html>
and<body>
) element that isn't.foo
.
Examples
Using :not() with valid selectors
This example shows some simple cases of using :not()
.
HTML
<p>I am a paragraph.</p>
<p class="fancy">I am so very fancy!</p>
<div>I am NOT a paragraph.</div>
<h2>
<span class="foo">foo inside h2</span>
<span class="bar">bar inside h2</span>
</h2>
CSS
.fancy {
text-shadow: 2px 2px 3px gold;
}
/* <p> elements that don't have a class `.fancy` */
p:not(.fancy) {
color: green;
}
/* Elements that are not <p> elements */
body :not(p) {
text-decoration: underline;
}
/* Elements that are not <div>s or `.fancy` */
body :not(div):not(.fancy) {
font-weight: bold;
}
/* Elements that are not <div>s or `.fancy` */
body :not(div, .fancy) {
text-decoration: overline underline;
}
/* Elements inside an <h2> that aren't a <span> with a class of `.foo` */
h2 :not(span.foo) {
color: red;
}
Result
Using :not() with invalid selectors
This example shows the use of :not()
with invalid selectors and how to prevent invalidation.
HTML
<p class="foo">I am a paragraph with .foo</p>
<p class="bar">I am a paragraph with .bar</p>
<div>I am a div without a class</div>
<div class="foo">I am a div with .foo</div>
<div class="bar">I am a div with .bar</div>
<div class="foo bar">I am a div with .foo and .bar</div>
CSS
/* Invalid rule, does nothing */
p:not(.foo, :invalid-pseudo-class) {
color: red;
font-style: italic;
}
/* Select all <p> elements without the `foo` class */
p:not(:is(.foo, :invalid-pseudo-class)) {
color: green;
border-top: dotted thin currentcolor;
}
/* Select all <div> elements without the `foo` or the `bar` class */
div:not(.foo, .bar) {
color: red;
font-style: italic;
}
/* Select all <div> elements without the `foo` or the `bar` class */
div:not(:is(.foo, .bar)) {
border-bottom: dotted thin currentcolor;
}
Result
The p:not(.foo, :invalid-pseudo-class)
rule is invalid because it contains an invalid selector. The :is()
pseudo-class accepts a forgiving selector list, so the :is(.foo, :invalid-pseudo-class)
rule is valid and equivalent to :is(.foo)
. Thus, the p:not(:is(.foo, :invalid-pseudo-class))
rule is valid and equivalent to p:not(.foo)
.
If :invalid-pseudo-class
was a valid selector, the first two rules above would still be equivalent (the last two rules showcase that). The use of :is()
makes the rule more robust.
Specifications
Specification |
---|
Selectors Level 4 # negation |
Browser compatibility
BCD tables only load in the browser
See also
-
Other functional CSS pseudo-classes:
-
How :not() chains multiple selectors on MDN blog (2023)