1

So I've been able to detect the number of siblings an element has by using nth-child(), however I haven't been able to figure out a way to select only the last few elements based on the total number of siblings.

In the following code snippet, I've achieved the goal I wanted by adding classes. But I would like a way to achieve this with just CSS selectors, if possible. In the following code snippet, each "group" has 2 rows, a row being defined as 3 elements together. I would like to select the orange elements as seen in the snippet below without adding classes. Either selecting the elements in the last row or selecting all of the elements except for those in the last row.

ul {
  padding-left: 0;
}

ul::after {
  content: '';
  display: table;
  clear: both;
}

li {
  background-color: #efefef;
  border: 1px dotted #000;
  box-sizing: content-box;
  float: left;
  list-style-type: none;
  padding: 10px 0;
  text-align: center;
  width: 33%
}

.alt {
  background-color: orange;
}
<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li class="alt">4</li>
</ul>
<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li class="alt">4</li>
  <li class="alt">5</li>
</ul>
<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li class="alt">4</li>
  <li class="alt">5</li>
  <li class="alt">6</li>
</ul>
Community
  • 1
  • 1
allejo
  • 2,076
  • 4
  • 25
  • 40
  • `:last-child` css selector? – Persijn Jul 01 '16 at 13:14
  • CSS can't detect the number of children or their position in a list. It can only style them based on selectors. If you can do that you might have a chance at it. – Paulie_D Jul 01 '16 at 13:18
  • Could you clarify the exact logic for what you are trying to do? –  Jul 01 '16 at 14:28
  • @torazaburo: If this question is anything like one I've previously seen (I'm still looking for it), it's asking how to match elements in the last "row" regardless of how many elements actually are in that row. So if each row has three elements at most, then match the last three elements if the last row also has three elements, match only the last two if the last row has only two elements, and match only the last element if the last row has only one element. The answer to that is entirely dependent on the number of elements in each row - along with the fact that this number is known in advance. – BoltClock Jul 01 '16 at 16:27
  • @BoltClock Thanks for the clarification, but I'm still having a mental block understanding it. What is "row" in this context? –  Jul 01 '16 at 16:45
  • @torazaburo: The snippet doesn't make it clear when viewed in that tiny little box because the scrollbar interferes with the layout - you'll have to expand the view to see the boxes laid out in rows of three. – BoltClock Jul 01 '16 at 16:58
  • @BoltClock Now I'm even more confused. So we are dealing with a question which depends on the particular wrapping behavior based on a particular screen width? Obviously CSS has no access to such information. –  Jul 01 '16 at 17:17

1 Answers1

2

Since nth-last-child can take a formula, you can do this for any number of elements using the same technique as in the other question you referenced.

ul {
  padding-left: 0;
}

ul::after {
  content: '';
  display: table;
  clear: both;
}

li {
  background-color: #efefef;
  border: 1px dotted #000;
  box-sizing: content-box;
  float: left;
  list-style-type: none;
  padding: 10px 0;
  text-align: center;
  width: 33%
}

/* 3 elements in last row */
li:first-child:nth-last-child(3n+0) ~ li:nth-last-child(1),
li:first-child:nth-last-child(3n+0) ~ li:nth-last-child(2),
li:first-child:nth-last-child(3n+0) ~ li:nth-last-child(3),

/* 2 elements in last row */
li:first-child:nth-last-child(3n+2) ~ li:nth-last-child(1),
li:first-child:nth-last-child(3n+2) ~ li:nth-last-child(2),

/* 1 element in last row */
li:first-child:nth-last-child(3n+1) ~ li:nth-last-child(1) {
  background-color: orange;
}
<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
</ul>
<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
  <li>5</li>
</ul>
<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
  <li>5</li>
  <li>6</li>
</ul>
<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
  <li>5</li>
  <li>6</li>
  <li>7</li>
  <li>8</li>
  <li>9</li>
  <li>10</li>
  <li>11</li>
  <li>12</li>
  <li>13</li>
</ul>

The expression li:first-child:nth-last-child(3) requires an li which is both the first-child of its parent and also the third from last child. These two constraints together mean that the parent element must have exactly 3 children. In similar fashion, li:first-child:nth-last-child(3n+0) means that the parent must have a number of children that is a multiple of 3.

Once we have constrained what first elements are valid, we then need to select the last element(s) in order to highlight them. The sibling selector (~) will select siblings of the matched element, and we use nth-last-child again to select the element N from the end.

These rules are combined and repeated to handle the case of 1, 2, or 3 elements in the last row.

Dark Falcon
  • 43,592
  • 5
  • 83
  • 98
  • 2
    While this may solve the presented problem, would you be able to explain how your posted CSS solves that problem, that way the OP - and future visitors - are able to learn a technique, or techniques, to solve their own future problems (without - necessarily - having to come back for more black-box code to solve the *next* similar problem) :) – David Thomas Jul 01 '16 at 13:21
  • @DavidThomas, I really cannot explain it much better than the original linked question, but I have tried. It is really something you either get or you don't (and then spend a long time reading the documentation to understand how the selectors work). – Dark Falcon Jul 01 '16 at 13:31
  • The explanation you've now added sounds reasonable to me. Note to readers that the sentence "In similar fashion, li:first-child:nth-last-child(3n+0) means that the parent must have a number of children that is a multiple of 3." implies that the number of children per row (or the coefficient) must be known in advance for this to work. – BoltClock Jul 01 '16 at 16:38