Kilian Valkhof

Front-end & user experience developer, Jedi.

The CSS3 :not() selector

CSS & HTML, 25 August 2008

There isn’t a lot of information to be found about the :not() selector. The specifications only offer 3 lines of text and a couple of examples. So lets see what it can do!

The Specification

The negation pseudo-class, :not(X), is a functional notation taking a simple selector […] as an argument. It represents an element that is not represented by the argument.

What it says here, is that a selector with a :not() in it will match all elements that do not match what’s between the parenthesis.

A simple selector is a term used in the specifications. A simple selector is: a single element, attribute selector, class, id or pseude-class.

Examples of simple selectors:

body
*
[value="foo"]
.foo
#foo
:hover

Basically, any of the above type, but with only one selector.

The browsers

The :not() selector is only supported by modern browsers (Firefox, Safari and Opera), :not(IE).

Let’s take a look at what browsers allow you to do:

div:not(.home) {…}

This selects all div elements that do not have the class .home

div *:not(p) em {…}

This selects all em elements that are in an element (that is not a p element) and that are in a div element. so <div><strong><em>…</em></strong></div> is a match, but <div><p><em>…</em></p></div> is not.

input:not([type="file"]) {…}

This uses the attribute selector to select all input element, save for the file upload ones.

I was surprised to find out that

h2:not(.foo, .bar) {…}

also works. You can give the :not() selector a comma separated list as well! (Tested in Firefox 3) This is surprising, because it’s not documented as such in the specifications.

You can use the :not() selector as a part of a large selector. I’ve done this a few times in my current design:

li:not(.pingback) .comment-content p:first-child:first-line {…}
body:not(.home) h2 + p:first-letter {…}
.post:not(.first-post) a:hover {…}

The :not() selector is a nice addition to the CSS Tookit, and it can already be used in a way that allows for graceful degradation, such as I do on this website. If you have any nice experiences with :not(), please share them in the comments!

Thanks for Reading!

I am Kilian Valkhof, a front-end and user experience developer from the Netherlands.
Contact me or ping me on twitter.

  1. DissidentRage

    The :not() selector is VERY handy for CSS-only menus. It allows you to add sub-menus without use of Javascript or additional blocks of CSS for each level of sub-menu.

    .menu ul {
    display: none;
    visibility: hidden;
    }
    .menu li:hover ul {
    display: block;
    visibility: visible;
    }
    .menu li:hover ul li:not(:hover) ul {
    display: none;
    visibility: visible;
    }

  2. Adamo Maisano

    The :not() selector is key to avoiding dishing out webfonts to certain mobile browsers that do not support them. It’s a fringe case, but all Android 2.1 browsers will not render any type of webfont. That doesn’t stop them from loading them however (and preventing the text from showing up at all until loaded), so I’ve used a UA tag in the body, “.android21,” and the body:not(.android21) rule to only send non Android 2.1 browsers the referenced name for my webfont. Android 2.1 gets the default font immediately, whether that’s set by the UA default style sheet, or a previous rule in the CSS file :)

  3. This is a really old article, but I’d like to specifically address the :not() pseudo-class accepting a comma-separated list of selectors, because people are still citing it today.

    You’re right in that it’s not documented in the CSS3 spec. The reason why it works in Firefox 3 is because it’s a bug. I’m not sure when the bug was fixed — it could be either in Firefox 3.1 or Firefox 3.5 — but it’s still nothing but a bug in Firefox 3.0. I haven’t seen any version of any other browser exhibit this bug.

    As a side note, the CSS working group has proposed an extension to the :not() pseudo-class in CSS4 that will let it accept a comma-separated list of simple selectors. It’s probably just a matter of time before it gets implemented, but in the meantime, the current version of :not() should not accept a comma-separated selector list in any current browser.

  4. Conrad Damon

    Any plans for supporting combinators? I’d like to be able to perform a selection such as “all TDs that are not descendants of a DIV with class name ‘foo'”. That request has also been framed as “turning off” styles that were selected at a more general level, so that you can revert to browser defaults.

  5. IE is fail, they need to get on with it and add CSS3 support immediately!

  6. Martin Ekström

    Well, IE does support it (IE 9 and forward), the problem is that the users must update their browsers manually and that they are not encouraged to do so. Complaining about IE is getting old, yes their old versions were problematic and they still are since people use them, but now they do follow the standards as good as any other vendor. As long as we have rendering engines that are not updated automatically web developers will have problem with backward compatibility – that is the big problem, not that “IE is fail”.

  7. also quite handy for an inline list of menu-items with seperators between the items

    nav li {
       display : inline;
    }
    nav li:not( :last-child ):after {
       content : ‘ | ‘;
    }

  8. […] 19.) CSS3 :not() 选择器 […]

  9. […] The CSS3 :not() selector […]

  10. Thanks for advanced tip. but it not work well in ie.

  11. Just perfect ! Thank you, it saves me a lot of time and a few million of brain cells

  12. Fantastic article. I wish there was a way to add a more complex statement like :not():until() so that css processing would bypass the matching elements in the “not” statement until it matches an element in the “until” statement.

  13. I’m using Chrome 23 and the *:not does not work on any circumstances. Even prepend with a div. Do you know why?

  14. serkan

    This works;

    .widget a:not(.link-button):hover

    but this one doesn’t;

    .widget a:not(.link-button, .tagcloud a):hover

    on any browser.

  15. eithed

    @Serkan – that’s because :not doesn’t handle multiple selectors within itself, ie. this – [quote]h2:not(.foo, .bar)[/quote] doesn’t work. It would be good to know what actually simple selector is :)

    It’s also worth noting that :not selector given styles are descendant, so, doing something like :not(.foo) {color: #ff0000} will affect everything in the document, simply because this targeting the body tag.

    http://jsfiddle.net/Bdr37/1/ – here’s a basic comparison :)

  16. BASTA!

    Is it possible to use double negation for set product? E.g. to select all elements after a p.foo AND an img.left, whichever comes last:

    p.foo ~ *:not(:not(img.left ~ *)) { color: red; }

    I wonder if this will work…

  17. Guy

    I would like to find all the elements of class .myClass who don’t have an sibling.

    But the following doesn’t work: ‘.myClass:not(>a)’ ?

  18. […] The CSS3 :not() selector There isn’t a lot of information to be found about the :not() selector. The specifications only offer 3 lines of text and a couple of examples. So lets see what it can do! […]