CSS specificity of :not() pseudo class - css

I have this small HTML:
<div id="column">
<div class="ticker">
<ul>
<li>Item 1</li>
</ul>
</div>
</div>
For ul elements outside of the .ticker class, but inside of the #column id exists this CSS:
#column ul:not(.a):not(.b) {
margin: 1em;
}
But inside the .ticker class I don't want this margin. So I thought I could use:
#column .ticker ul {
margin: 0;
}
That said, I know that the specificity of the first CSS selector is higher because of the two :not() pseudo classes. But to get a higher specificity I had to append those two :not() in the second CSS snippet to the ul, too. So that works:
#column .ticker ul:not(.c):not(.d) {
margin: 0;
}
Isn't that stupid? In fact it doesn't matter what you use in the two :not()pseudo classes. They just have to be there. This doesn't make any sense to me.
Is that simply a part of CSS3 which is not perfect or is there a solution which my brain didn't come up with yet?
See it in action here: http://jsfiddle.net/9BDw5/2/

It's not just you; this is indeed one of the fundamental pitfalls of specificity as a CSS concept.
A simpler solution that is equally valid would be to repeat your .ticker class selector so you have this:
#column .ticker.ticker ul {
margin: 0;
}
This way you do not have to modify your element to add an extra class just for the sake of increasing selector specificity.
The spec verifies this:
Note: Repeated occurrances of the same simple selector are allowed and do increase specificity.
On a side note, remember that the specificity of the :not() pseudo-class is strictly defined (in the same section of the spec) as equal to that of its argument. So :not(#id) and #id have the same specificity, and likewise for :not(E):not(.a) and E.a. The :not portion does not count at all, not even as a pseudo-class.
This limitation in specificity will be addressed in Selectors 4, which enhances :not() to accept a comma-delimited list of selectors. The specificity of a :not() that contains a selector list will be that of the most specific selectors in the list, so the specificity of ul:not(.c, .d) is equal to 1 type selector and 1 class selector, compared to ul:not(.c):not(.d) which is equal to 1 type selector and 2 class selectors. This makes it tremendously useful in excluding any number of classes from a match.

Here is an alternative, and somewhat cleaner approach.
Add a fictitious class name to your div.ticker element:
<div id="column">
<ul>
<li>Outside Item 1</li>
</ul>
<div class="ticker extra-tag">
<ul>
<li>Item 1</li>
</ul>
</div>
</div>
and modify the CSS as follows:
#column ul:not(.a):not(.b) {
margin-left: 1em;
}
#column .ticker.extra-tag ul {
margin-left: 0;
}
So, the specificity of the first rule is 1-1-2 and for the second rule 1-1-2.
The two :not() count as two classes, so you need to have at least two classes in
the second rule, which I implemented by using the fictitious class name .extra-tag.
The fictitious class is probably more computationally efficient than adding the
extra :not() pseudo-class.
See demo at: http://jsfiddle.net/audetwebdesign/7beNx/
Learn more about CSS specificity at: http://www.w3.org/TR/css3-selectors/#specificity

As mentioned above chaining the :not() pseudo-class increases specificity by one each time.
This article explains it very nicely

Related

:not selector is changing the evaluated order of equivalent selectors [duplicate]

This question already has answers here:
What is the most character-efficient way to increase CSS specificity?
(4 answers)
Closed 4 years ago.
I have an issue where I'm working in a rather large CSS code base that makes frequent use of overriding previously defined classes/selectors. As such, it is very sensitive to the order in which they are defined.
Here is a sample of how I need it to work
.grid {
padding:25px;
background-color: red;
}
.grid {
padding:50px;
background-color: green;
}
<li>
<div class="grid">
Test
</div>
</li>
Notice how the second .grid definition overrides the first.
This is what is happening now:
.grid:not(.ui) {
padding:25px;
background-color: red;
}
.grid {
padding:50px;
background-color: green;
}
<li>
<div class="grid">
Test
</div>
</li>
Using the :not pseudo-class hover moves the evaluated priority to after normal class definitions. I need it to be evaluated in the same order as before, but I need the :not selector. Are there any solutions besides refactoring?
The :not rule is more specific, so it takes higher precedence.
If you can't refactor, you could put a bogus :not condition on the other rule as well, so they'll have the same precedence and thus revert to document order:
.grid:not(.ui) {
padding:25px;
background-color: red;
}
.grid:not(.nonexistentclassname) {
padding:50px;
background-color: green;
}
<li>
<div class="grid">
Test
</div>
</li>
You just need to make the selector you want to take precedence be more specific than the other one. If you add a "dummy" class to the element, you can add that class to your second selector to make it more specific (or at least make a tie where then, the last selector wins).
CSS Specificity is calculated as follows:
1000 points for an inline style
100 points for an id in the selector
10 points for a class or pseudo-class in the selector
1 point for an element or pseudo-element in the selector
In your case:
.grid:not(.ui)
Is worth 20 points because the selector has 1 class and one pseudo-classes in it.
But:
.grid
is only worth 10 points because of the one class.
/* This selector is worth 20 points */
.grid:not(.ui) {
padding:25px;
background-color: red;
}
/* This selector is also worth 20 points, but becomes it comes
after the other one, the location breaks the tie. */
.grid.special {
padding:50px;
background-color: green;
}
<li>
<!-- Adding another "dummy" class to the element allows you
to correctly find it with your CSS, and do it with a more
specific selector, if needed. -->
<div class="grid special">
Test
</div>
</li>
And, what if you need (for some reason) have the order of the selectors reversed? Just make the one that is suppose to "win" a little more specific:
/* This selector is worth 21 points */
div.grid.special {
padding:50px;
background-color: green;
}
/* This selector is worth 20 points */
.grid:not(.ui) {
padding:25px;
background-color: red;
}
<li>
<!-- Adding another "dummy" class to the element allows you
to correctly find it with your CSS, and do it with a more
specific selector, if needed. -->
<div class="grid special">
Test
</div>
</li>
Here's a great site for understanding how specificity is calculated that let's you "play" with selectors.
In your first example the .grid selectors each have a specificity value of 10 (classes = 10). Therefore, since both rules have the same specificity, their source order decides.
In your second rule, .grid:not(.ui) has a specificity value of 20 (2 classses; the :not() pseudo-class has no specificity value). The source order is subordinate because the rules have different specificity values.
So, to achieve your goal (the same behavior as before but with :not() applied to the first rule), you need to boost the specificity of the second rule by at least 10.
One method would be to add a useless :not() to the second rule. This method is described in another answer and is allowed by the spec:
6.6.7. The negation
pseudo-class
Note: the :not() pseudo allows useless selectors to be written. For
instance :not(*|*), which represents no element at all, or
foo:not(bar), which is equivalent to foo but with a higher
specificity.
.grid:not(.ui) {
padding:25px;
background-color: red;
}
.grid:not(.bar) {
padding:50px;
background-color: green;
}
<div class="grid">Test</div>
specificity calculator

set only the css style of first adjacent div with a class name

How can I set the style of only the first div that has class "bla"? (not the second).
<div class="outer">
<div>
....(more div's, unknown how many)
<div class="bla">
....
<div class="bla">some content</div>
</div>
....
</div>
</div>
I'm assuming with this answer that by adjacent elements you mean sibling elements. If you were referring to parent-child elements then go with N1xx1's answer. That being said...
You can't target the first bla with css selectors alone. But you can target all the blas but the first. So, one possibility is to set the styles you want only on the first bla on all blas. Then override those styles by targeting all blas but the first. Like so:
.bla {
...styles for the first bla..
}
.bla ~ .bla {
...override styles set on first bla that you dont want on the others
}
The tilde between the two ".bla"'s is called the general sibling selector. If you've never heard of it, head on over to css selectors spec.
You can do simple workaround for this since you can't do that with any special selector:
.bla {
/* style here, example: */
background-color: #f00;
}
.bla .bla {
/* negate the style, example: */
background-color: transparent;
}
I hope this is what you were looking for.
According to pure css, you can't select according to the ordering of the html elements. Search the spec (here: http://www.w3.org/TR/CSS2/selector.html). There is nothing that refers to how many or in what order html elements match the given selectors.
Javascript:
getElementsByClass('bla')[0].style
EDIT: JOPLOmacedo provided a CSS only (better) answer
I've also found a way to select for instance the second <p> after a <h1> tag:
h1 + p + p{
background: red;
}
Just thought I'd share that.

Is it possible to chain :not() then :last-child?

I need to style (CSS only) the last child element while excluding those with a specific class.
For example, I want to style
<ul>
<li>Bar</li>
<li>Bar</li>
<li>Bar</li>
<li>Should have a green background</li>
<li class='foo'>Bar</li>
</ul>
The last li without class 'foo' should be green. I tried
li:not(.foo):last-child {
background-color: green;
}​
or
li:not(.foo):last-of-type {
background-color: green;
}​
but it doesn't works.
See http://jsfiddle.net/gentooboontoo/V7rab/2/
The answer to your question Is it possible to chain :not() then :last-child? (or, more simply, can pseudo-classes be chained?) is very much a yes. But as others have pointed out, if an li:last-child has an id="foo" then nothing will be selected. As a demonstration, a similar expression
li:not(.bar):last-child {
background-color: green;
}​
works just fine. The problem is that successive selectors all apply to the entire context, not to a subset specified by previous expressions so li:not(.foo):last-child is identical to li:last-child:not(.foo), which is clearly not what is required.
I don't think that will work (it doesn't work, but I don't think it should work anyway)
The selector is working, but the second-to-last li is never going to be the :last-child because it isn't the last-child...
It isn't like jQuery's not() method which actually removes the specified element from the selection. The CSS :not selector/filter will ignore the element, but not remove it from the page
There is only one last child inside any element. In your example, it's <li class='foo'>Bar</li>. If you have exact number of children though, you could use adjacent-sibling combinator:
LI:first-child + LI + LI + LI {/* here are declarations for 4th LI. */}
Applying 'last-child' will give browser issue. so I tried in Jquery.
If you need use this code.
var a = 0
$('ul li').each(function() {
$(this).attr("id",a);
a++;
});
var b = a-1;
$('#'+b).attr('style','background-color:green');​

How do I select the "last child" with a specific class name in CSS? [duplicate]

This question already has answers here:
Can I combine :nth-child() or :nth-of-type() with an arbitrary selector?
(8 answers)
Closed 3 years ago.
<ul>
<li class="list">test1</li>
<li class="list">test2</li>
<li class="list">test3</li>
<li>test4</li>
</ul>
How do I select the "last child" with the class name: list?
<style>
ul li.list:last-child{background-color:#000;}
</style>
I know the example above doesn't work, but is there anything similar to this that does work?
IMPORTANT:
a) I can't use ul li:nth-child(3), because it could happen that it's on the fourth or fifth place too.
b) No JavaScript.
This can be done using an attribute selector.
[class~='list']:last-of-type {
background: #000;
}
The class~ selects a specific whole word. This allows your list item to have multiple classes if need be, in various order. It'll still find the exact class "list" and apply the style to the last one.
See a working example here: http://codepen.io/chasebank/pen/ZYyeab
Read more on attribute selectors:
http://css-tricks.com/attribute-selectors/
http://www.w3schools.com/css/css_attribute_selectors.asp
You can use the adjacent sibling selector to achieve something similar, that might help.
.list-item.other-class + .list-item:not(.other-class)
Will effectively target the immediately following element after the last element with the class other-class.
Read more here: https://css-tricks.com/almanac/selectors/a/adjacent-sibling/
This is a cheeky answer, but if you are constrained to CSS only and able to reverse your items in the DOM, it might be worth considering. It relies on the fact that while there is no selector for the last element of a specific class, it is actually possible to style the first. The trick is to then use flexbox to display the elements in reverse order.
ul {
display: flex;
flex-direction: column-reverse;
}
/* Apply desired style to all matching elements. */
ul > li.list {
background-color: #888;
}
/* Using a more specific selector, "unstyle" elements which are not the first. */
ul > li.list ~ li.list {
background-color: inherit;
}
<ul>
<li class="list">0</li>
<li>1</li>
<li class="list">2</li>
</ul>
<ul>
<li>0</li>
<li class="list">1</li>
<li class="list">2</li>
<li>3</li>
</ul>
You can't target the last instance of the class name in your list without JS.
However, you may not be entirely out-of-css-luck, depending on what you are wanting to achieve. For example, by using the next sibling selector, I have added a visual divider after the last of your .list elements here: http://jsbin.com/vejixisudo/edit?html,css,output
$('.class')[$(this).length - 1]
or
$( "p" ).last().addClass( "selected" );
I suggest that you take advantage of the fact that you can assign multiple classes to an element like so:
<ul>
<li class="list">test1</li>
<li class="list">test2</li>
<li class="list last">test3</li>
<li>test4</li>
</ul>
The last element has the list class like its siblings but also has the last class which you can use to set any CSS property you want, like so:
ul li.list {
color: #FF0000;
}
ul li.list.last {
background-color: #000;
}

CSS selector to apply rules to an element if before another element

I'm looking for a way to apply some CSS to elements differently, depending on what follows it. For example, with this HTML:
<ul>
<li>
<span class="title">Some Title</span>
<span class="subtitle">With Some Subtitle</span>
</li>
<li>
<span class="title">Only a Title</span>
</li>
</ul>
I want to apply different rules to .title depending on wether or not there is a .subtitle.
The closest I can figure out is the adjacent sibling selector
.title + .subtitle { /* rules */ }
But that applies the rules to the .subtitle elements that are preceded by a .title. Instead I want the rule to apply to .title elements with a .subtitle element after it.
NOTE: it's not vital this is widely supported by browsers for my current usage. My only target that matters is webkit based at the moment.
There's no sibling-combinator for "older" siblings in the CSS3 spec.
In this case, you might be able to get away with :only-child.
li > span.title:only-child { /* rules */ }
I think this would require backtracking in the layout engine and thus isn't available. you could do this in jQuery rather simply however:
$('span.title + span.subtitle').prev().addClass('cssClass')

Resources