I'm probably answering my own question, but I'm extremely curious.
I know that CSS can select individual children of a parent, but is there support to style the children of a container, if its parent has a certain amount of children.
for example
container:children(8) {
// style the parent this way if there are 8 children
}
I know it sounds weird, but my manager asked me to check it out, haven't found anything on my own so I decided to turn to SO before ending the search.
Clarification:
Because of a previous phrasing in the original question, a few SO citizens have raised concerns that this answer could be misleading. Note that, in CSS3, styles cannot be applied to a parent node based on the number of children it has. However, styles can be applied to the children nodes based on the number of siblings they have.
Original answer:
Incredibly, this is now possible purely in CSS3.
/* one item */
li:first-child:nth-last-child(1) {
/* -or- li:only-child { */
width: 100%;
}
/* two items */
li:first-child:nth-last-child(2),
li:first-child:nth-last-child(2) ~ li {
width: 50%;
}
/* three items */
li:first-child:nth-last-child(3),
li:first-child:nth-last-child(3) ~ li {
width: 33.3333%;
}
/* four items */
li:first-child:nth-last-child(4),
li:first-child:nth-last-child(4) ~ li {
width: 25%;
}
The trick is to select the first child when it's also the nth-from-the-last child. This effectively selects based on the number of siblings.
Credit for this technique goes to André Luís (discovered) & Lea Verou (refined).
Don't you just love CSS3? 😄
CodePen Example:
https://codepen.io/mattlubner-the-decoder/pen/ExaQZQR
Sources:
http://andr3.net/blog/post/142 (André Luís)
http://lea.verou.me/2011/01/styling-children-based-on-their-number-with-css3/ (Lea Verou)
No. Well, not really. There are a couple of selectors that can get you somewhat close, but probably won't work in your example and don't have the best browser compatibility.
:only-child
The :only-child is one of the few true counting selectors in the sense that it's only applied when there is one child of the element's parent. Using your idealized example, it acts like children(1) probably would.
:nth-child
The :nth-child selector might actually get you where you want to go depending on what you're really looking to do. If you want to style all elements if there are 8 children, you're out of luck. If, however, you want to apply styles to the 8th and later elements, try this:
p:nth-child( n + 8 ){
/* add styles to make it pretty */
}
Unfortunately, these probably aren't the solutions you're looking for. In the end, you'll probably need to use some Javascript wizardry to apply the styles based on the count - even if you were to use one of these, you'd need to have a hard look at browser compatibility before going with a pure CSS solution.
W3 CSS3 Spec on pseudo-classes
EDIT I read your question a little differently - there are a couple other ways to style the parent, not the children. Let me throw a few other selectors your way:
:empty and :not
This styles elements that have no children. Not that useful on its own, but when paired with the :not selector, you can style only the elements that have children:
div:not(:empty) {
/* We know it has stuff in it! */
}
You can't count how many children are available with pure CSS here, but it is another interesting selector that lets you do cool things.
NOTE: This solution will return the children of sets of certain lengths, not the parent element as you have asked. Hopefully, it's still useful.
Andre Luis came up with a method: http://lea.verou.me/2011/01/styling-children-based-on-their-number-with-css3/ Unfortunately, it only works in IE9 and above.
Essentially, you combine :nth-child() with other pseudo classes that deal with the position of an element. This approach allows you to specify elements from sets of elements with specific lengths.
For instance :nth-child(1):nth-last-child(3) matches the first element in a set while also being the 3rd element from the end of the set. This does two things: guarantees that the set only has three elements and that we have the first of the three. To specify the second element of the three element set, we'd use :nth-child(2):nth-last-child(2).
Example 1 - Select all list elements if set has three elements:
li:nth-child(1):nth-last-child(3),
li:nth-child(2):nth-last-child(2),
li:nth-child(3):nth-last-child(1) {
width: 33.3333%;
}
Example 1 alternative from Lea Verou:
li:first-child:nth-last-child(3),
li:first-child:nth-last-child(3) ~ li {
width: 33.3333%;
}
Example 2 - target last element of set with three list elements:
li:nth-child(3):last-child {
/* I'm the last of three */
}
Example 2 alternative:
li:nth-child(3):nth-last-child(1) {
/* I'm the last of three */
}
Example 3 - target second element of set with four list elements:
li:nth-child(2):nth-last-child(3) {
/* I'm the second of four */
}
Working off of Matt's solution, I used the following Compass/SCSS implementation.
#for $i from 1 through 20 {
li:first-child:nth-last-child( #{$i} ),
li:first-child:nth-last-child( #{$i} ) ~ li {
width: calc(100% / #{$i} - 10px);
}
}
This allows you to quickly expand the number of items.
Now we can use the :has() selector to identify the number of items and apply style to the container
.container {
height: 50px;
margin: 10px;
}
.container:not(:has(*)) { /* 0 elements */
background: yellow;
}
.container:has(> :last-child:nth-child(1)) { /* 1 element */
background: red;
}
.container:has(> :last-child:nth-child(2)) { /* 2 elements */
background: blue;
}
.container:has(> :last-child:nth-child(3)) { /* 3 elements */
background: green;
}
/* For N elements
.container:has(> :last-child:nth-child(N)) {
background: red;
}
*/
<div class="container">
</div>
<div class="container">
<div></div>
</div>
<div class="container">
<div></div>
<div></div>
</div>
<div class="container">
<div></div>
<div></div>
<div></div>
</div>
Yes we can do this using nth-child like this:
div:nth-child(n + 8) {
background: red;
}
This will make the 8th div child onwards become red. Hope this helps...
Also, if someone ever says "hey, they can't be done with styled using css, use JS!" doubt them immediately. CSS is extremely flexible nowadays
.container div {
background: blue;
}
.container div:nth-child(n + 8) {
background: red;
}
<div class="container">
<div>div 1</div>
<div>div 2</div>
<div>div 3</div>
<div>div 4</div>
<div>div 5</div>
<div>div 6</div>
<div>div 7</div>
<div>div 8</div>
<div>div 9</div>
<div>div 10</div>
<div>div 11</div>
<div>div 12</div>
</div>
In the example the first 7 children are blue, then 8 onwards are red...
[External example]
If you are going to do it in pure CSS (using scss) but you have different elements/classes inside the same parent class you can use this version!!
&:first-of-type:nth-last-of-type(1) {
max-width: 100%;
}
#for $i from 2 through 10 {
&:first-of-type:nth-last-of-type(#{$i}),
&:first-of-type:nth-last-of-type(#{$i}) ~ & {
max-width: (100% / #{$i});
}
}
If you're looking for a way to style all elements if more than N exist (e.g. 2 or more):
li:first-child:nth-last-child(n+2),
li:first-child:nth-last-child(n+2) ~ li {
background-color: red;
}
<ul>
<li>first</li>
</ul>
<ul>
<li>first</li>
<li>second</li>
</ul>
<ul>
<li>first</li>
<li>second</li>
<li>third</li>
</ul>
No, there is nothing like this in CSS. You can, however, use JavaScript to calculate the number of children and apply styles.
You can use :has selector like this:
.parent-element:has(:nth-child(8))
It selects element with .parent-element class that has child number 8. If child number 8 does not exist, rule will not be applied.
Unfortunately :has is not supported in firefox by default. You can still use it and add extra tricky rule for firefox support.
If you are using something like reactJS, you can simply add a class and then style it.
e.g. suppose you are displaying items from an array.
app.jsx
<div className={`items-count-is-${items.length}`}>
{items.map(item => {
return <li>...</li>;
})}
</div>
e.g. CSS: items-count-is-5 {...}
Related
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>
I'm trying to use nth child to select every other TWO divs.
This means, when there is a collection of divs 1, 2, 3, 4, 5, and 6 - I need to select 1, 2, 5 and 6. Every other two.
CSS:
#navigation .menuItem:nth-child(3n+3) {
background-color: #ccc;
}
HTML:
<div class="menuItemWrapper">
<div class="menuItem">Shop Online</div>
<div class="menuItem">The Blog</div>
<div class="menuItem">LookBook</div>
<div class="menuItem">Gift Finder</div>
<div class="menuItem">About Us</div>
<div class="menuItem sub">Tutorials</div>
<div class="menuItem sub">FAQ</div>
<div class="menuItem sub">Contact</div>
JS Fiddle
You won't be able to do it with a single selector, but you can with two:
#navigation .menuItem:nth-child(4n+1), #navigation .menuItem:nth-child(4n+2) {
background-color: #ccc;
}
Fiddle
There is a simple hack for this:
Firstly: set global color for your lined-up <div>s
.menuItemWrapper>div { background-color: #fff; }
Secondly: select every 4th element and apply alternative background to <div> element directly after it. That makes pairs. -1 to begin from the right place.
.menuItemWrapper>div:nth-child(4n-1) { background-color: #ccc; }
.menuItemWrapper>div:nth-child(4n-1)+div { background-color: #ccc; }
Enjoy!
#Francesca here's a simple and awesome tool for :nth tester by css-trickes, its making the life easier for selectors css-tricks nth-child-tester
Expanding the response from Chowlett to manage the posibility to "skip" elements.
Change the elements that you want to skin from div to nav (this would be pretty much the same).
Reference them by using nth-of-type selector instead of nth-child. and made the selector more specific to avoid changing the background of the navs
.menuItemWrapper div.menuItem:nth-of-type(4n+1),
.menuItemWrapper div.menuItem:nth-of-type(4n+2) {
background-color: #ccc;
}
modyfied demo
(item 5 is skipped)
I think I found a solution to your problem based on Chowlett suggestion: http://jsfiddle.net/Z8Uxt/10/.
All it requires from you is to change the divider to different element type (eg: p instead of div or maybe even the semantic div instead of li).
The solution uses :nth-of-type as :nth-child count every child regardless of its class.
I'm probably answering my own question, but I'm extremely curious.
I know that CSS can select individual children of a parent, but is there support to style the children of a container, if its parent has a certain amount of children.
for example
container:children(8) {
// style the parent this way if there are 8 children
}
I know it sounds weird, but my manager asked me to check it out, haven't found anything on my own so I decided to turn to SO before ending the search.
Clarification:
Because of a previous phrasing in the original question, a few SO citizens have raised concerns that this answer could be misleading. Note that, in CSS3, styles cannot be applied to a parent node based on the number of children it has. However, styles can be applied to the children nodes based on the number of siblings they have.
Original answer:
Incredibly, this is now possible purely in CSS3.
/* one item */
li:first-child:nth-last-child(1) {
/* -or- li:only-child { */
width: 100%;
}
/* two items */
li:first-child:nth-last-child(2),
li:first-child:nth-last-child(2) ~ li {
width: 50%;
}
/* three items */
li:first-child:nth-last-child(3),
li:first-child:nth-last-child(3) ~ li {
width: 33.3333%;
}
/* four items */
li:first-child:nth-last-child(4),
li:first-child:nth-last-child(4) ~ li {
width: 25%;
}
The trick is to select the first child when it's also the nth-from-the-last child. This effectively selects based on the number of siblings.
Credit for this technique goes to André Luís (discovered) & Lea Verou (refined).
Don't you just love CSS3? 😄
CodePen Example:
https://codepen.io/mattlubner-the-decoder/pen/ExaQZQR
Sources:
http://andr3.net/blog/post/142 (André Luís)
http://lea.verou.me/2011/01/styling-children-based-on-their-number-with-css3/ (Lea Verou)
No. Well, not really. There are a couple of selectors that can get you somewhat close, but probably won't work in your example and don't have the best browser compatibility.
:only-child
The :only-child is one of the few true counting selectors in the sense that it's only applied when there is one child of the element's parent. Using your idealized example, it acts like children(1) probably would.
:nth-child
The :nth-child selector might actually get you where you want to go depending on what you're really looking to do. If you want to style all elements if there are 8 children, you're out of luck. If, however, you want to apply styles to the 8th and later elements, try this:
p:nth-child( n + 8 ){
/* add styles to make it pretty */
}
Unfortunately, these probably aren't the solutions you're looking for. In the end, you'll probably need to use some Javascript wizardry to apply the styles based on the count - even if you were to use one of these, you'd need to have a hard look at browser compatibility before going with a pure CSS solution.
W3 CSS3 Spec on pseudo-classes
EDIT I read your question a little differently - there are a couple other ways to style the parent, not the children. Let me throw a few other selectors your way:
:empty and :not
This styles elements that have no children. Not that useful on its own, but when paired with the :not selector, you can style only the elements that have children:
div:not(:empty) {
/* We know it has stuff in it! */
}
You can't count how many children are available with pure CSS here, but it is another interesting selector that lets you do cool things.
NOTE: This solution will return the children of sets of certain lengths, not the parent element as you have asked. Hopefully, it's still useful.
Andre Luis came up with a method: http://lea.verou.me/2011/01/styling-children-based-on-their-number-with-css3/ Unfortunately, it only works in IE9 and above.
Essentially, you combine :nth-child() with other pseudo classes that deal with the position of an element. This approach allows you to specify elements from sets of elements with specific lengths.
For instance :nth-child(1):nth-last-child(3) matches the first element in a set while also being the 3rd element from the end of the set. This does two things: guarantees that the set only has three elements and that we have the first of the three. To specify the second element of the three element set, we'd use :nth-child(2):nth-last-child(2).
Example 1 - Select all list elements if set has three elements:
li:nth-child(1):nth-last-child(3),
li:nth-child(2):nth-last-child(2),
li:nth-child(3):nth-last-child(1) {
width: 33.3333%;
}
Example 1 alternative from Lea Verou:
li:first-child:nth-last-child(3),
li:first-child:nth-last-child(3) ~ li {
width: 33.3333%;
}
Example 2 - target last element of set with three list elements:
li:nth-child(3):last-child {
/* I'm the last of three */
}
Example 2 alternative:
li:nth-child(3):nth-last-child(1) {
/* I'm the last of three */
}
Example 3 - target second element of set with four list elements:
li:nth-child(2):nth-last-child(3) {
/* I'm the second of four */
}
Working off of Matt's solution, I used the following Compass/SCSS implementation.
#for $i from 1 through 20 {
li:first-child:nth-last-child( #{$i} ),
li:first-child:nth-last-child( #{$i} ) ~ li {
width: calc(100% / #{$i} - 10px);
}
}
This allows you to quickly expand the number of items.
Now we can use the :has() selector to identify the number of items and apply style to the container
.container {
height: 50px;
margin: 10px;
}
.container:not(:has(*)) { /* 0 elements */
background: yellow;
}
.container:has(> :last-child:nth-child(1)) { /* 1 element */
background: red;
}
.container:has(> :last-child:nth-child(2)) { /* 2 elements */
background: blue;
}
.container:has(> :last-child:nth-child(3)) { /* 3 elements */
background: green;
}
/* For N elements
.container:has(> :last-child:nth-child(N)) {
background: red;
}
*/
<div class="container">
</div>
<div class="container">
<div></div>
</div>
<div class="container">
<div></div>
<div></div>
</div>
<div class="container">
<div></div>
<div></div>
<div></div>
</div>
Yes we can do this using nth-child like this:
div:nth-child(n + 8) {
background: red;
}
This will make the 8th div child onwards become red. Hope this helps...
Also, if someone ever says "hey, they can't be done with styled using css, use JS!" doubt them immediately. CSS is extremely flexible nowadays
.container div {
background: blue;
}
.container div:nth-child(n + 8) {
background: red;
}
<div class="container">
<div>div 1</div>
<div>div 2</div>
<div>div 3</div>
<div>div 4</div>
<div>div 5</div>
<div>div 6</div>
<div>div 7</div>
<div>div 8</div>
<div>div 9</div>
<div>div 10</div>
<div>div 11</div>
<div>div 12</div>
</div>
In the example the first 7 children are blue, then 8 onwards are red...
[External example]
If you are going to do it in pure CSS (using scss) but you have different elements/classes inside the same parent class you can use this version!!
&:first-of-type:nth-last-of-type(1) {
max-width: 100%;
}
#for $i from 2 through 10 {
&:first-of-type:nth-last-of-type(#{$i}),
&:first-of-type:nth-last-of-type(#{$i}) ~ & {
max-width: (100% / #{$i});
}
}
If you're looking for a way to style all elements if more than N exist (e.g. 2 or more):
li:first-child:nth-last-child(n+2),
li:first-child:nth-last-child(n+2) ~ li {
background-color: red;
}
<ul>
<li>first</li>
</ul>
<ul>
<li>first</li>
<li>second</li>
</ul>
<ul>
<li>first</li>
<li>second</li>
<li>third</li>
</ul>
No, there is nothing like this in CSS. You can, however, use JavaScript to calculate the number of children and apply styles.
You can use :has selector like this:
.parent-element:has(:nth-child(8))
It selects element with .parent-element class that has child number 8. If child number 8 does not exist, rule will not be applied.
Unfortunately :has is not supported in firefox by default. You can still use it and add extra tricky rule for firefox support.
If you are using something like reactJS, you can simply add a class and then style it.
e.g. suppose you are displaying items from an array.
app.jsx
<div className={`items-count-is-${items.length}`}>
{items.map(item => {
return <li>...</li>;
})}
</div>
e.g. CSS: items-count-is-5 {...}
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.
This is HTML.
<div class="container">
<div> background of this i need in white </div>
<div> background of this i need in red </div>
<div> background of this i need in white </div>
<div> background of this i need in red </div>
</div>
I want to select alternate div without adding class or id .
Is it possible with CSS only (no Javascript) with IE 7 support
IE7 doesn't support the selector you would require, which is :nth-child().
Generally you would use
.container div:nth-child(even) {
background: red;
}
IE7 does not support it, unfortunately.
You will need to use JavaScript, or add a class to every odd or even row (perhaps using a server side language).
can't we select every second div inside <div class="container"> [with the CSS2 selectors introduced by IE7]?
Well kind of, with the adjacency selector:
.container div { background: white; }
.container div+div { background: red; }
.container div+div+div { background: white; }
.container div+div+div+div { background: red; }
But that means writing out a rule (of increasingly unwieldy length) for each child. The above covers the example markup with four children, so it's manageable for short, fixed-number-of-children elements, but impractical for elements with a large or unlimited number of children.
This cannot be done.
Use in-line style tags, like,
the following works in IE 7
not tested for others.
<div style="background-color:#ffff00" > Hello YOU div</div>
div:nth-child(odd) { background-color:#ffffff; }
div:nth-child(even) { background-color:#ff0000; }
but i don't know (and can't test) if this works in IE7 - if not, you'll have to use different classes for the divs.