Do parent selectors take precedence over child selector in CSS? - css

I had a question about how CSS selectors work between parent and children, and which one would take precedence over the other.
<div class="red">
<div class="blue">
<div class="green">
</div>
</div>
</div>
If you then have
.red .green{
border: 1px solid red;
}
.blue .green{
border: 1px solid blue;
}
Which one would take effect? And to override a CSS style does it have to be as specific a selector as the one you're trying to override?

You should read up on specificity.
To answer your immediate question, all your selectors carry the same specificity, so in the case of .green, the last rule takes precedence: your border would be blue.

the parent selectors encompass everything within those tags that aren't assigned a value. So in your case the 2nd , you wanted to add text or images there, it would follow in line with that class. The third would do the same. Think of it like a math equation and parenthesis:
5 x 4 x (3x3) = 180
you would do the parenthesis first then the other factors.

The position of an element has no effect on selector specificity if you are simply using ascendant/descendant selectors. Note that
.red, .green
.blue, .green
Select elements separately because of the comma. That is, the <div class=red> and <div class=green> are both selected, and this would happen no matter if one were a descendant of the other. I think that you mean to remove the comma.
In that case, the order of the declaration of the rules will take effect with later rules having higher precedence. That is .blue, .green has higher precedence over .red, .green even though the specificity is the same simply because it is declared later.

Related

The order of a descendant combinator CSS definition [duplicate]

This question already has answers here:
Understanding CSS selector priority / specificity
(4 answers)
Closed 4 years ago.
This must be a very simple question for HTML ninjas out there, but I feel I'm missing something obvious here. Here is a snippet:
#red span {
color: red;
}
#green span {
color: green;
}
<div id="red">
<p><span>red</span></p>
<div id="green">
<p><span>green</span></p>
</div>
</div>
If I swap the stylesheet order, all of the text becomes red:
#green span {
color: green;
}
#red span {
color: red;
}
<div id="red">
<p><span>red</span></p>
<div id="green">
<p><span>green</span></p>
</div>
</div>
This happens despite the fact that <div id="green"> is a more inner parent of <span>green</span> than <div id="red"> in the DOM tree. I suppose it doesn't take precedence simple because its CSS now appears first in the order of stylesheets. So the order of stylesheets is what matters here.
Is this an expected behavior? Is this implementation/browser specific? Is there some official specs detailing that?
Finally, is there any CSS selector syntax I can use to make it work as in the first snippet, without relying on the order of stylesheets or adding new class names, ids, etc?
Yes, the result you got is absolutely expected—well, maybe not expected, but they are correct. Here are the official specs. And here’s a tweet poll of mine detailing the exact same problem. (Spoiler: the majority of voters got it wrong.) Read the replies for a more in-depth discussion.
Currently, there’s not any CSS technology that takes “closest parent” scope into account. And this is a common misconception a lot of programmers have. (CSS is not a programming language.) A typical programmer will think, “The selector #red span means wherever I see a #red, look for a span inside, and then apply the styles. Since #green span is inside the #red, the green will apply after the red.” This is simply incorrect.
The way CSS actually applies styles is that it looks at each element, then goes through the stylesheets from top to bottom, decides if it matches, and then applies/overrides styles as it goes. That’s just one aspect of the cascade, among others (such as inheritance and specificity). Since in your second example #red span comes last in the CSS source, it gets applied last, overriding #green span, regardless of “how close” the span is within the #red in the DOM.
To fix your specific problem, the easiest thing to do is use a direct child selector, like #red > p > span and #green > p > span. But as you’d suspect, these selectors would have to be updated if you ever change the HTML. Coupling your CSS and HTML is a hassle, especially as your project grows.
The best strategy is not to depend on the DOM to style your elements. What happens when you move the span outside the #red? Would you want it to keep its style? For maintainable and scalable CSS, you should use classes only (not IDs) and apply the class to the actual element you want styled, without depending on DOM structure or parent-child relationships. That way, when your HTML structure changes, you don’t have to adjust your CSS to match.
Example:
.red {
color: red;
}
.green {
color: green;
}
<div>
<p><span class="red">red</span></p>
<div>
<p><span class="green">green</span></p>
</div>
</div>
You can use > selector to make it apply to only specific span inside the div with the id you give and not all the span inside the div
#green > span {
color: green;
}
span {
color: red;
}
<div id="red">
<p><span>red</span></p>
<div id="green">
<span>green</span>
</div>
</div>
If you want to do it to all descendants, you can achieve it by Javascript like following, it decreases many line of codes if you want to apply many colors, but will be slower time than css. It's up to your preferences
var colors = ['red', 'green', 'blue', 'orange', 'brown']
var spans = document.querySelectorAll('span');
spans.forEach(function(spanElement) {
colors.forEach(function(color){
if(spanElement.closest(`#${color}`)){
spanElement.style.color=color
}
})
})
<div id="red">
<span>red</span>
<div id="green">
<span>green</span>
<p><span>green</span></p>
</div>
<div id="blue">
<span>blue</span>
</div>
<div id="orange">
<span>orange</span>
</div>
<div id="brown">
<span>brown</span>
</div>
</div>

How does specificity work with inherited styles?

HTML
<div class='container'>
<p>foo</p>
</div>
CSS
.container {
color: red;
}
p {
color: blue
}
Code Pen
The applied color is blue. Why is this? I was thinking that since .container has more specificity than p, the color would end up being red.
What is happening here? Why is it blue?
My hypothesis is that the process is "Does p have any selectors? If so use it and don't look up for .container. If it didn't have any styles, it'd look up and use the style for .container."
From the MDN page on Specifity
Styles for a directly targeted element will always take precedence over inherited styles, regardless of the specificity of the inherited rule.
https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity
Hence p will override .container no matter what. The inherited style from .container is overwritten
think of the css in this specific case as a target bulls eye.
you start from the most specific pointer to your element in question.
in your example above, it is the p selector since the text is within the p tag wrapper. then going outward from that (the ring right outside the bulls eye, if you will) is the .container div. since the target p is closest to the text you want to color, it inherits that css (color text of blue).
in the example below:
.container {
color: red;
}
p {
color: blue
}
<div class='container'>
i should be red text since im outside the p tag but inside the div container
<p>foo</p>
</div>
you see that the text "i should be red...." doesnt necessarily have a "bulls eye" css, so it goes one ring outside and sees .container and is assigned the color red.
========
now to answer your question about specificity
specificity applies to a case like the example below:
.makeMeBlue {
color: blue;
}
.makeMeBlue.actuallyMakeMeRed {
color: red;
}
<div class="makeMeBlue">i am some random text</div>
<div class="makeMeBlue actuallyMakeMeRed">some more text here</div>
in the above example, you see that make .makeMeBlue has css to make the color of the text blue. however, the second .makeMeBlue div's text color is red. this is because we were more specific about targeting the second element. we used the selector .makeMeBlue.actuallyMakeMeRed utilizing both classes of the element to say "this is the element i want to target specifically and assign this css to".
so instead of the element being like "developers are blue, ok i'll be blue" it sees "hey, someone just said all developers who are named 'jason' are red, and my name is jason and i'm a developer - it is more specific to me, so i'll be red".
i hope that explained specificity a little more clearly.

CSS: Does the plus sign work with pseudo elements?

For markup similar to this:
<div>
<p>hello world</p>
</div>
<div>
<h4>hello world</h4>
</div>
Can you do something like this in CSS:
div:after {
content: "";
display: block;
border-bottom: 2px solid red;
}
p + div:after {
content: "";
display: block;
border-bottom: 2px solid blue;
}
...meaning to say "Give all :after pseudo elements immediately following a <p> a blue border. Give all others a red border".
This doesn't seem to work. I realize this is because the + sign is applying to the 'div' selector, not the 'div:after' selector as a whole. But is there another way to target these in CSS (without adding a new class specific to these instances and without manipulating the DOM)?
Basically, what Michael_B said:
You can't target a pseudo-element. A pseudo-element is added to a selector that has matched an element.
"Target" is a vague term, but the second sentence is on point here. Combinators only work with elements, because selectors match elements, not pseudo-elements. What you're really trying to do in selector nomenclature is to style the ::after pseudo-element of a div whose last child is a p element (in which case the ::after box immediately follows the p box in the formatting tree):
<div>
<p>hello world</p>
div::after <!-- Blue border -->
</div>
<div>
<h4>hello world</h4>
div::after <!-- Red border -->
</div>
And you can't do that, because there is no parent selector.
I imagine something like div:has(> p:last-child)::after from Selectors 4 will work, but it depends on whether :has() makes it into CSS in the first place. The only other good option is to figure out which of these div elements has a p as their last child and assign them a special class name.
See also:
Can I target a :before or :after pseudo-element with a sibling combinator?
Is there a CSS parent selector?

How to set borders between children from parent in CSS?

For setting a border line between elements, I use border on one side for each child, except the last one. For example
<div class="parent">
<div>First</div>
<div>Second</div>
<div>Third</div>
<div>Fourth</div>
</div>
with CSS
.parent div{
display:block;
padding:5px;
border-bottom:dashed 1px #000}
.parent div:last-child{
border-bottom:dashed 0 #000
}
Is there a way to set the border between children from parent's CSS style? without using last-child. In other words, in one single statement from parent rule.
No, the border is a property of the child element, and thus can only be specified on them. You can use a single rule for this, but it requires advanced CSS3 selector support:
.parent > div:not(:last-child){
border-bottom: dashed 1px #000;
}
I just know a workaround: use jQuery and iterate through those child elements(each: http://api.jquery.com/each/) and set your css class if next(next: http://api.jquery.com/?s=next) element is also child...
I think another way, just using css does not exist, but I'm not sure, if you find a solution with css only, please post it ;)
Greetings

CSS :first-child to work with classes

Say i have this markup:
<div class='current'>
</div>
<div class='current'>
</div>
<div class='current'>
</div>
<div class='current'>
</div>
<div class='current'>
</div>
Now these divs are not necessarily next to each other in the markup, but could be spread throughout the page.
Can i target only the first occurrence of class "current" using CSS only, i'd ideally like to avoid using javascript (for now)?
Ie.
.current:first-child {
background: red;
}
I believe you're looking for something like this:
.current:nth-child(1){
background:red;
}
Should do the trick!
:first-child targets elements that are first children, not first occurrence of a given class. So this will target all elements with current class, that are first children. It can be all of them if they are in different places on a page or none at all.
It sounds like you may be looking for css3 selector first-of-type
As mentioned in these two answers (along with this new one), CSS3 doesn't bake in a pseudo-class that selects the first element of its class (unlike :first-of-type which selects by type).
You can always use :first-child if .current is guaranteed to be the first child of .group:
.group .current:first-child {
background: red;
}
But if it's not guaranteed to be, then based on your comments and the answer link, since they all share the same parent you can do this instead:
.group .current {
background: red;
}
.group .current ~ .current {
background: transparent; /* Or whatever your default is */
}
The general sibling combinator ~ ignores other elements that may not be .current. All these rules work in IE7+.
If they are spread throughout the page, you can not get what you need with pure CSS solution. Even with first-of-type unless the elements are on the same DOM level. Check the example to see that you can not select the elements.
On the other hand once I move the third .current to the same DOM level where I already have the second one, I get only the second item selected, as it's the first .current on this level.
On the other hand it's a very short one-liner in JS
Don't overcomplicate things ;)
If it's spread throughout the page, you can't target it with css.

Resources