A Complete Guide to How the CSS not Selector Works
Tags: css, webdev, tutorial, programming
Published: 2022-08-06 15:22:00

In CSS, it's often necessary to sign post 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.

## CSS not Selector
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:
```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:
```css
div:not(.not-red) {
    color: red;
}
```

Here's another example. By default, all elements will have the Arial font - except for `.old-fashioned` elements:
```html
<div class="old-fashioned">Old Fashioned Text</div>
<div>Some Text</div>
```

Our CSS, where only non `.old-fashioned` elements use the Arial font, looks like this:
```css
div:not(.old-fashioned) {
    font-family: Arial, sans-serif;
}
```

## CSS not Selectors Increase Specificity
You might be familiar with the concept of specificity in CSS, where certain selectors "override" others. For example, a `class` has a 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()`

## CSS not Selectors and the DOM structure
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:
```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:

```css
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:
```html
<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`.

## CSS not Selector Support
**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. [You can see the full support for the :not selector here](https://caniuse.com/css-not-sel-list).