421

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) .child {
  //style the children 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.

Temani Afif
  • 245,468
  • 26
  • 309
  • 415
Brodie
  • 8,399
  • 8
  • 35
  • 55
  • 4
    Quantity Query SCSS Mixin: http://codepen.io/jakob-e/pen/wgGpeP – Jakob E Apr 13 '17 at 12:11
  • Jun 3 '20 edit by @vsync should be reverted, as it changes the code sample in a way that does not agree in any way with the question itself or its answers. – Nicole Ashley Jan 13 '21 at 23:53
  • @NikRolls - My edit should ***not be reverted***. I merely condensed the problem to its essence - which is *how to select a parent according to number of child nodes* it has. If you can do that, then obviously you can do what the OP wanted in the first place, but the **core** is that selector itself and not anything that comes after it. – vsync Jan 14 '21 at 09:11

12 Answers12

879

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:

Sources:

Matthemattics
  • 9,577
  • 1
  • 21
  • 18
  • The OP has since edited their question as well. I cleaned up the comments, but for the sake of clarity I think I'll leave your edit in. You can revert it on your own volition if you find it superfluous. – BoltClock Dec 25 '14 at 04:47
  • @BoltClock Thanks, I've updated my answer to reduce any future confusion. – Matthemattics Dec 29 '14 at 20:16
  • 17
    Very handy. I wrote a SASS mixin that makes use of this technique: http://codepen.io/anon/pen/pJeLdE?editors=110 – SimpleJ Jun 03 '15 at 21:33
  • This answer is incorrect. On the latest version of Chrome, li:first-child:nth-last-child(1) calls ALL children instead of one. Downvote this, please. – Ian Steffy Sep 07 '15 at 10:17
  • 1
    @IanSteffy I just tested this on Chrome 45.0.2454.85 (64-bit) and it works fine… ? – Matthemattics Sep 09 '15 at 00:30
  • 1
    @IanSteffy I added a codepen… feel free to fork it w/ an example of this technique not working so we can see what you mean – Matthemattics Sep 09 '15 at 00:49
  • 2
    If it works in codepen but not with my project then it is most definitely my fault. My bad. This answer is correct! Correct, I say! – Ian Steffy Sep 09 '15 at 13:30
  • 1
    @IanSteffy :D no worries, dude! – Matthemattics Sep 09 '15 at 23:43
  • 1
    @Hypermattt there is a way to achive this for "three or more children"? – robsonrosa Nov 04 '15 at 16:52
  • @robsonrosa Hmm… maybe style for a default case of "three or more siblings", then reset the specific cases of just one, and just two, siblings? – Matthemattics Dec 03 '15 at 20:46
  • 4
    Might be nice to add why this works, for people not used to multiple pseudo selectors (like I was until now ;). What is happening here is that this selects the child that is at the same time child x from the start/top and child y from the end. And so this only selects something if there are exactly x+y children. – Legolas Aug 29 '16 at 18:23
  • 9
    Note that instead of `:first-child:nth-last-child(1)` you can also use `:only-child`. – Adam Reis Sep 04 '17 at 23:08
  • Here's a nice expanded example page on this technique: https://www.growingwiththeweb.com/2014/06/detecting-number-of-siblings-with-css.html – Joe DF Aug 13 '18 at 20:15
  • Why are the near duplicate `~` rules necessary for two+ rules? They seem redundant. The code pen example doesn't change for me if they are removed. – AnnanFay Jan 06 '20 at 08:15
  • 1
    @AnnanFay Because `li:first-child:nth-last-child` only selects the first element, not all of them. The general sibling combinator (`~`) is needed to select all of the elements. – Matthemattics Jan 07 '20 at 19:15
  • @Matthemattics This is such an excellent solution! Thank you! – Reena Verma Feb 08 '22 at 16:35
  • 1
    Just to point out that with the new `:has()` selector being implanted in many browsers, CSS can now select parent. – jimmymcheung Feb 20 '23 at 18:46
63

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.

derekerdmann
  • 17,696
  • 11
  • 76
  • 110
  • It's worth noting the original question was edited, rendering the initial "No" a bit misleading (just fyi ) – Matthemattics Apr 19 '16 at 15:12
  • Beware! The last solution (with `:not(:empty)`) will not work if one would like to style only elements with "real" content (concrete elements for example) - as it will style also `div`s with only whitespaces or newline characters for example: https://jsfiddle.net/c69kbuxm/ . Considering that the statement "This styles elements that have no children." may be misleading I believe. Thank You for the answer anyway. – kcpr Sep 18 '22 at 23:39
28

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 */
}
ifugu
  • 648
  • 7
  • 11
  • 2
    again, this is number of siblings, not children – Tomas Jan 18 '14 at 15:14
  • @TMS see accepted answer's edit - the OP's question was awkwardly phrased – ifugu Jul 02 '14 at 22:39
  • This approach is perfect for when each element needs to be styled differently based on its position in the list, e.g. to get even circular transforms: `li:nth-child(3):nth-last-child(1) { transform: rotate(120deg); } li:nth-child(2):nth-last-child(2) { transform: rotate(240deg); }` and so on... – Greg Smith Aug 11 '16 at 08:26
18

Now we can use the :has() selector to identify the number of items and apply style to the container as well as the child items

.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>
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
  • 1
    It's still in very early stage. It should not be used on Production Level https://caniuse.com/css-has – Airy Nov 11 '22 at 14:18
13

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.

mwtidd
  • 131
  • 1
  • 3
6

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]

Teocci
  • 7,189
  • 1
  • 50
  • 48
AlanFoster
  • 8,156
  • 5
  • 35
  • 52
  • 1
    Perhaps add a note about the unfortunate [lack of support](http://www.quirksmode.org/css/contents.html#t38)? – benesch Jan 04 '12 at 01:35
  • 5
    I don't think this is exactly what Brodie is asking - this will style the _children_ after a given amount, but can't select the ancestor/containing element based on the number of its children. – Ben Hull Jan 04 '12 at 01:36
  • This is actually some pretty good information though, thanks alan but bee is right, I was trying to say "if an element has this many children us this style" If I'm not mistaken your method would style the 8th child on, but the first 7 would be lonely and naked. – Brodie Jan 04 '12 at 01:43
  • @primatology - The only browser that doesn't support nth-child is IE<9. All others have been supporting it two versions back or more. – Rob Jan 04 '12 at 01:53
  • -1 This will not make **div child** red, this will make the div red based on its number within its **siblings**! Is not related to div's child in any way! – Tomas Jan 18 '14 at 15:09
5

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});
    }
  }
4

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>
fredrivett
  • 5,419
  • 3
  • 35
  • 48
  • Can this be used to select and style only the 6th element, if it is also the 5th from the last? If so, then, for example, when you had pagination on mobile that showed too many pages, you could set some of the center ones to display:none. – F. Certainly. Aug 14 '21 at 23:25
  • @F.Certainly. that's quite a different requirement, but is possible with `li:nth-child(6):nth-last-child(5) { background-color: red }` https://jsfiddle.net/0eahnrfd/ – fredrivett Aug 17 '21 at 10:38
  • For some reason that wouldn't set the display property to none, but the original solution turned out for the better – F. Certainly. Aug 22 '21 at 19:18
  • The some reason probably would be specificity issues, with another set of rules overriding it if `background-color: red` or similar worked. – fredrivett Aug 23 '21 at 15:18
2

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.

  • That;s a great solution. `:has()` CSS pseudo class can be enabled on Firefox by navigating to `about:config` in a new window and toggling the ``layout.css.has-selector` attribute to `true`. – Aurovrata Jun 15 '23 at 08:02
1

No, there is nothing like this in CSS. You can, however, use JavaScript to calculate the number of children and apply styles.

Niet the Dark Absol
  • 320,036
  • 81
  • 464
  • 592
0

If You want to stylize differently based on child count in specific range,
You can use
(this example counts just span childs not having "hidden" class
if You want less specific selector omit the part after nth-child index)

.selector {
    /* default style for 0 - 9 childs */
}

.selector:has(:nth-child(10 of span:not(.hidden))) {
    /* style for 10 - 19 childs (10th child exists) */
}

.selector:has(:nth-child(20 of span:not(.hidden))) {
    /* style for 20 - 29 childs (20th child exists) */
}

.selector:has(:nth-child(30 of span:not(.hidden))) {
    /* style for 30 and more childs (30th child exists) */
}
-2

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 {...}

Nadim
  • 75
  • 1
  • 5
  • You do not want to hard code endless CSS rules to simply accommodate numbers. This solution would be better if you included rules with variable numbers, which are entirely possible using SCSS or the like. – Kees Briggs Nov 17 '22 at 19:16