In CSS, it's often necessary to signpost that we want to select an element, but not in specific circumstances - like if it has a certain class. In these instances, we can use the :not()
selector to do that. Let's look at how it works.
The CSS :not()
selector is broadly supported by most browsers. The way it works is that we create a selector, and then specify what it should not be. For example, say you have the following HTML:
<div class="not-red">Not Red</div>
<div class="not-red">Not Red</div>
<div>Red</div>
<div>Red</div>
We have a bunch of div
s here, and some shouldn't be red. If we want all div
s in general on our page to be red, except for .not-red
elements, we can use :not(.not-red)
to ensure they remain, well, not red:
div:not(.not-red) {
color: red;
}
Here's another example. By default, all elements will have the Arial font - except for .old-fashioned
elements:
<div class="old-fashioned">Old Fashioned Text</div>
<div>Some Text</div>
Our CSS, where only non .old-fashioned
elements use the Arial font, which looks like this:
div:not(.old-fashioned) {
font-family: Arial, sans-serif;
}
You might be familiar with the concept of specificity in CSS, where certain selectors "override" others. For example, a class
has lower specificity than an id
, so any id
CSS properties will override class
properties on the same element.
The :not
selector also affects specificity. For example, if you had div:not(#id)
in your code, it still counts as having an id
, so the specificity increases as if it has an id
. This is useful to remember when using :not()
One confusing thing about :not()
, is when you try to use it to stop styles applying to things within elements. For example, suppose you have the following HTML:
<div class="container">
<form>
<div class="input-element">
<input type="text" />
</div>
</form>
<div class="input-element">
<input type="text" />
</div>
</div>
Let's say you want to apply a style to only input
elements which are not within form
elements. Simple, right? You might try something like this:
div :not(form) input {
border: 2px solid red;
}
Only this won't work, and the reason why is that :not()
is applied at every level - and we are wrapping each input in .input-element
. That means that :not(form)
is applied to .input-element
, and it is indeed, not a form. So both input
elements will have a red border. To avoid that, you need to remove the wrapper element and have the input be a direct child of the form:
<div class="container">
<form>
<input type="text" />
</form>
<input type="text" />
</div>
That way the input in the form, will not inherit the styles from div :not(form) input
.
It is important to note that there are two versions of the :not()
selector - one which accepts only a single, simple CSS selector, and the newer update to :not()
which accepts any CSS selector.
All browsers support single, simple :not()
selectors, like the ones we've used so far, including Internet Explorer. However, not all browsers support complex selectors. With simple selectors in :not
, you can do all of the following:
:not(#id)
:not(.class)
:not(element)
:not([attrbute])
However, things like the following are only available with complex selectors:
:not(#id, .class, [attribute])
:not(#id.class)
:not(element#id, #id.class)
Fortunately, support for complex selectors is still quite high. The only browser not supporting complex selectors today is Internet Explorer and some mobile browsers:
Also published here.