How do CSS selectors actually work? - css

I've looked around for information about this, but since I'm not sure of the technical names of the process, I can't seem to find an answer.
Let say I have this:
.some_class {}
.some_class .sub_div {}
<div class="some_class">
<div class="intermediate_div">
<div class="sub_div">
From my understanding, the selector .some_class .sub_div should apply to the div 'sub_div'. But it appears this is not the case as I always have to go back and change it to '.some_class .intermediate_div .sub_div'.
Is this correct or am I missing something?
My impression was that .some_class .sub_div should apply to any instance of .sub_div within the .some_class element.

.some_class .sub_div would work without any confusion. But you're actually right that .some_class .intermediate_div .sub_div would work but .some_class .sub_div wouldn't because of css specificity.
Look below for an example:
div.intermediate_div .sub_div {
color: red;
}
.some_class .sub_div {
color: blue;
}
What would you think color blue is applied? No. The red color is applied because div.intermediate_div .sub_nav higher specificity than .some_class .sub_div and thus in that case you may be wondering to see that
.some_class .intermediate_div .sub_div{
color: blue;
}
works. Because it has more specificity than previous selector.
Look more about css specificity here and in my previous answer.

CSS will apply the most specific rule to each element.
In the case of two equally specific rules, it will apply the last defined rule.
Take a look at this fiddle, which uses the following html:
<div class="some_class">
Some class
<div class="intermediate_div">
Intermediate
<div class="sub_div">Sub Div</div>
</div>
</div>
and this CSS:
.some_class {
color: red;
}
.intermediate_div {
background-color: blue;
}
.intermediate_div .sub_div {
background-color: orange;
}
.some_class .sub_div {
background-color: green;
}
All of the text is red, because some_class is red, and everything inside that div inherits that style.
intermediate_div has a blue background, which would be inherited by sub_div. But sub_div has two more specific rules defined.
Both of those rules with two class selectors are equally specific. Both are more specific than the blue background because they select based on two classes not one. The orange background is ignored, because the last defined rule is used when they are equally specific.
When you use an element id, that is more specific than a class because ids are (supposed to be) unique. So in this modified fiddle, you can see that sub_div takes styles from #div1 even though that rule is defined first and only has one selector. It still takes precedence over rules defined later and rules with two selectors, because it uses a unique id.

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

CSS :FirstChild in same section with 2 class not working

Color is not applied as per CSS on the 3rd row, using first child(div.multiple-alerts .normal:first-child span).
https://jsfiddle.net/Lh6cpzeb/
div.multiple-alerts .high:first-child span{ color: yellow; }
div.multiple-alerts .normal:first-child span{ color: yellow; }
<div class="multiple-alerts">
<div class="cls high"><span>high</span></div>
<div class="cls high"><span>high</span></div>
<div class="cls normal"><span>normal</span></div>
<div class="cls normal"><span>normal</span></div>
</div>
The CSS is being applied the correct way, but I think your understanding of how the rules work may be slightly off. You're selecting the first child of all divs with class multiple-alerts which also has the class of normal. Well, the first child of multiple-alerts does not have the class normal (at least in the snippet you included), so your selector matches exactly zero elements.
Now, you may be tempted to go for something like first-of-type, but that only applies to tags, not classes. So, here's a workaround that you might find useful:
Let's say the standard colour for these spans is black, we will set all the spans inside .normal with yellow colour, then override it for all but the first one, like so:
div.multiple-alerts .normal span {
color: yellow;
}
div.multiple-alerts .normal ~ .normal span {
color: black;
}
If you're not sure how this is working here, the ~ works similarly to the +, but is broader. The + can only match with the very next sibling, whereas the ~ can match with any succeeding sibling - i.e. after, but not before.
:nth-child(i) selector will solve the problem
div.multiple-alerts .cls:nth-child(3) span{ color: yellow; }

How do I have a CSS classes priority be affected by what class it is "more closely" inside?

Up until just a few moments ago, I thought for sure that selector "distance" affected which css styles would be applied. Here's what I mean by distance:
.class-2 .target {
background-color: green;
}
.class-1 .target {
background-color: red;
}
<div class="class-1">
<div class="class-2">
<p class="target">Hello World!</p>
</div>
</div>
In this example, I guess I expected that the .target element would have a green background color--since it seems like the .class-2 .target style is more specific--at least, target is more closely inside class-2 than class 1. But this is not the case. Apparently the only thing affecting the priority is the order they were declared in.
This seems really strange to me; I guess I assumed that CSS rules were applied from the outside in, or at least that that was a factor.
What do I do when I need a classes styles to be applied based on which class it is more closely inside. Is there any way to do this?
For example, in this JSFiddle, how would I get the backgrounds to be appropriately red and green colored? https://jsfiddle.net/emsca2ww/3/
In my specific case I need this because I am using a generally 12 column grid, and I need to (in some situations) set a 16 column grid context inside that.
In this specific case you can use the child selector: >
https://jsfiddle.net/emsca2ww/7/
.class-2 > .target {
background-color: green;
}
.class-1 > .target {
background-color: red;
}
This only works for parent/child elements. Otherwise you would have to introduce more parent/child relationships if needed or rethink how you are using the selectors.
Selectors have specificity and cascade order. The above selectors have the same specificity because they are both composed of two classes. This falls back to cascade order. They exist in the same stylesheet as well, so the final priority rule is applied: order in the CSS document.
If you want .class-2 to have higher priority than .class-1, you have to move the selector after it in the stylesheet:
.class-1 .target {
background-color: green;
}
.class-2 .target {
background-color: red;
}
However, this has nothing to do with the HTML itself. There is no selector for closeness between parents and children in the HTML document. You could do something like:
.class-2 > * > .target
But this selector only works if .target is a grandchild.
There is no distance priority in CSS, the only rules for priority are:
the number of ids
the number of classes, pseudo-classes
the number of elements, pseudo-elements,
the * selector
as defined in http://www.w3.org/TR/selectors/#specificity
To achieve what you want, you need to add some classes in your css:
<div class="foo class-1">
<div class="foo class-2">
<p class="target">Hello World!</p>
</div>
</div>
.class-1 .target, .foo .class-1 .target, .foo .foo .class-1 .target {
background-color: red;
}
.class-2 .target, .foo .class-2 .target, .foo .foo .class-2 .target {
background-color: green;
}
By exemple. But if you have a lot of nesting, it could become a nightmare, and you should consider another way.

Unusual CSS specificity behaviour

I've worked with CSS for ages and I thought I understood specificity well, but this example baffles me; it's late at night so I might be missing something obvious:
.class span {
color: blue;
}
section#id {
color: beige;
}
<div class="class">
<section id="id">
<span>Test</span>
</section>
</div>
Specificity for section#id is 101, while for .class span is 11 and, on top of that, the second rule is even specified after the first one.
What obvious thing am I missing?
The first rule is narrowed to the span tag. The second rule is a higher level, at the parent section. So, yes, .class span will take precedence because it's hitting the actual tag.
You aren't targeting the span with the second selector. The color will only cascade to elements where the color property is set to inherit (default).

Is there any real advantage to using nested selectors?

if #div2 is contained within #div1 is there any real advantage to referring to that in the css stylesheet? like this:
#div1 {
display:block;
}
#div1 #div2 {
background-color:#e0e0e0;
}
and then similarly. i have a table ID and then it's table headers
#myTable {
width:100%;
}
#myTable #productName{
width:75%;
}
The only advantage is that you can be more specific, for example create a rule that applies to some element only if it placed inside another one. But in most cases, there is no reason for that, and it may hurt the performance of your css (plus it creates code duplication, in case you want to change the parent ID).
In your case the selectors are IDs, and since there can be only one element with each ID, there are even less advantages for nested selectors, and probably you don't need them.
Read this article for more deep explanations.
As element-IDs are unique in the DOM the selected context is unambiguous in the given examples and there is no advantage whatsoever.
However, when you are working with classes nested selectors become very useful.
Generally its not useful to nest id selectors, but there are subtle differences, in the following, the nested rule has a higher specificity and the background will be red
HTML
<div id="outer">
<div id="inner">foo</div>
</div>
CSS
#outer #inner {
background-color: red;
}
#inner {
background-color: blue;
}
The rules for specificity are outline in the css spec.
That being said, classes are usually the way to go, but this can be useful for changing the styles of elements based on their container (if for some odd reason you cant/wont use classes)

Resources