I have this layout:
<div id="sectors">
<h1>Sectors</h1>
<div id="s7-1103" class="alpha"></div>
<div id="s8-1104" class="alpha"></div>
<div id="s1-7605" class="beta"></div>
<div id="s0-7479"></div>
<div id="s2-6528" class="gamma"></div>
<div id="s0-4444"></div>
</div>
With these style rules:
#sectors {
width: 584px;
background-color: #ffd;
margin: 1.5em;
border: 4px dashed #000;
padding: 16px;
overflow: auto;
}
#sectors h1 {
font-size: 2em;
font-weight: bold;
text-align: center;
}
#sectors div {
float: left;
position: relative;
width: 180px;
height: 240px;
margin: 16px 0 0 16px;
border-style: solid;
border-width: 2px;
}
#sectors div:nth-of-type(3n+1) {
margin-left: 0;
}
#sectors div::after {
display: block;
position: absolute;
width: 100%;
bottom: 0;
font-weight: bold;
text-align: center;
text-transform: capitalize;
background-color: rgba(255, 255, 255, 0.8);
border-top-style: solid;
border-top-width: 2px;
content: attr(id) ' - ' attr(class);
}
#sectors div.alpha { color: #b00; background-color: #ffe0d9; }
#sectors div.beta { color: #05b; background-color: #c0edff; }
#sectors div.gamma { color: #362; background-color: #d4f6c3; }
#sectors div.unassigned {
color: #808080;
background-color: #e9e9e9;
opacity: 0.5;
}
#sectors div.unassigned::after {
content: attr(id) ' - Unassigned';
}
#sectors div.unassigned:hover {
opacity: 1.0;
}
I use jQuery to add the unassigned class to sectors that don't otherwise have one of the classes alpha, beta or gamma:
$('#sectors div:not(.alpha, .beta, .gamma)').addClass('unassigned');
And everything works flawlessly in modern browsers.
But seeing as the :not() selector in jQuery is based on :not() in CSS3, I thought I could place it directly in my stylesheet so I wouldn't have to rely on an extra class added using jQuery. Besides, I'm not really interested in supporting older versions of IE, and other browsers have excellent support for the :not() selector.
So I try doing this (knowing I will only have sectors Α, Β and Γ in my layout):
#sectors div:not(.alpha, .beta, .gamma) {
color: #808080;
background-color: #e9e9e9;
opacity: 0.5;
}
#sectors div:not(.alpha, .beta, .gamma)::after {
content: attr(id) ' - Unassigned';
}
#sectors div:not(.alpha, .beta, .gamma):hover {
opacity: 1.0;
}
But then it suddenly stops working — in all browsers! My unassigned sectors aren't grayed out, faded out or labeled 'Unassigned' anymore.
Updated but not so interactive jsFiddle preview
Why is the selector failing here? Shouldn't :not() work identically in both jQuery and CSS since jQuery borrows from the CSS3 standard, or is there something I'm missing? Is there even a workaround for this if I really want to avoid relying on a script?
Why is the selector failing here? Shouldn't
:not()work identically in both jQuery and CSS since jQuery borrows from the CSS3 standard, or is there something I'm missing?
Perhaps it should, but it turns out that it doesn't. jQuery extends the :not() selector such that you can pass any selector to it, no matter how complex it may be, and I suspect that this is for parity with the .not() method, which also accepts any arbitrarily complex selector and filters accordingly. It does in a way maintain a CSS-like syntax, but it extends from what's defined in the standard.
As another example, this works just fine (I know it's an incredibly ludicrous example compared to what's given in the question, but it's just for illustrative purposes):
/*
* Select any section
* that's neither a child of body with a class
* nor a child of body having a descendant with a class.
*/
$('section:not(body > [class], body > :has([class]))')
Remember that passing a comma-separated list of selectors to :not() means filtering elements that don't match any of the listed selectors.
Now the :not() pseudo-class in CSS3, on the other hand, is very limited by itself. You can only pass a single simple selector as an argument to :not(). This means you can pass only any one of these at a time:
Universal selector (
*), optionally with a namespaceType selector (
a,div,span,ul,li, etc), optionally with a namespaceAttribute selector (
[att],[att=val], etc), optionally with a namespaceClass selector (
.class)ID selector (
.id)Pseudo-class (
:pseudo-class)
So, here are the differences between jQuery's :not() selector and CSS3's :not() selector:
First and foremost, to answer the question directly: you can't pass a comma-separated selector list.1 For example, while the given selector works in jQuery as demonstrated in the fiddle, it isn't valid CSS3:
/* If it's not in the Α, Β or Γ sectors, it's unassigned */ #sectors div:not(.alpha, .beta, .gamma)Is there even a workaround for this if I really want to avoid relying on a script?
Thankfully, in this case, there is. You simply have to chain multiple
:not()selectors, one after another, in order to make it valid CSS3:#sectors div:not(.alpha):not(.beta):not(.gamma)It doesn't make the selector that much longer, but the inconsistency and inconvenience remain evident.
You can't combine simple selectors into compound selectors for use with
:not(). This works in jQuery, but is invalid CSS3:/* Do not find divs that have all three classes together */ div:not(.foo.bar.baz)You'll need to split it up into multiple negations (not just chain them!) to make it valid CSS3:
div:not(.foo), div:not(.bar), div:not(.baz)As you can see, this is even more inconvenient than point 1.
You can't use combinators. This works in jQuery, but not CSS:
/* * Grab everything that is neither #foo itself nor within #foo. * Notice the descendant combinator (the space) between #foo and *. */ :not(#foo, #foo *)This one is particularly nasty, primarily because it has no proper workaround. There are some loose workarounds (1 and 2), but they usually depend on the HTML structure and are therefore very limited in utility.
It's worth noting that the CSS4 Selectors spec enhances the :not() selector to allow a comma-separated list of compound selectors. Compound selectors are, quite simply, simple selectors combined (compounded) together, without combinators separating them. This means that the jQuery selectors shown in points 1 and 2 above will probably become equivalent and valid CSS4 as well, which will make the pseudo-class much, much more useful when CSS parsers start to support it in the coming years.
Unfortunately, it still does not allow combinators as shown in point 3, at least not as of this writing, so jQuery still has kind of the upper hand here. That shouldn't be much of a problem though, IMO, as you should rarely ever need to use combinators within the :not() selector (but that's just my take on it).2
1 Although this article says that you can pass a comma-separated list of selectors to :not() in Firefox 3, you're not supposed to be able to. It worked because of a bug in Firefox 3 for which I can't find the ticket anymore, but it shouldn't work until future browsers implement future standards. Seeing how often that article is cited to date, I've left a comment to this effect, but seeing also how old the article is and how infrequently the site is being updated, I'm really not counting on the author coming back to fix it.
2 On the other hand, I have no personal objections to using the .not() method with complex selectors and combinators myself. As a matter of fact, the documentation for jQuery's :not() selector states: "The .not() method will end up providing you with more readable selections than pushing complex selectors or variables into a :not() selector filter. In most cases, it is a better choice."







.




