andrux andrux - 3 months ago 19
CSS Question

CSS super advanced nth-child selector?

I'm currently working on a photo gallery with pagination. It shows the main image and also a list of thumbnails, something like this:

+----------------------+----+----+----+---+----+
| | 1 | 2 | 3 | 4 | 5 |
+ +----+----+----+---+----+
| MAIN IMAGE | 6 | 7 | 8 | 9 | 10 |
+ +----+----+----+---+----+
| | 11 | 12 | 13 | < | > |
+----------------------+----+----+----+---+----+


Please note the numbers in the thumbnail container are a number assigned to each image, NOT the actual position in the container.

The thumbnails I have them inside a list container, so they are targeted using
ul > li
selectors.

So, my problem is that I need to give different margins to the list items containing my images, depending on the position in their container, for example:

margin-left: 0px
for images 1, 6, and 11

margin-right: 0px
for images 5, 10 and the > character at position 15

So, I'm able to do part of what I need, by using :nth-child selector, something like this:

ul li:nth-child(5n+1) {
margin-left: 0;
}

ul li:nth-child(5n+5) {
margin-right: 0;
}


The problem comes when I go to the next page of thumbnails, my
<
and
>
links I have to move them from page 1 to page 2, and then hide all thumbnails in page 1 to show the ones in page 2, so the selectors no longer apply... it's complicated, so it's better to show what page 2 would look like:

+----------------------+----+----+----+----+----+
| | 14 | 15 | 16 | 17 | 18 |
+ +----+----+----+----+----+
| MAIN IMAGE | 19 | 20 | 21 | 22 | 23 |
+ +----+----+----+----+----+
| | 24 | 25 | 26 | < | > |
+----------------------+----+----+----+----+----+


So, now my selectors won't work, instead of applying to images 15, 20 and 25 for the left margin and 21, 26 and the
>
link for the right margin, they apply to 14, 19, 24 for one margin, and 18, 23 and the
>
link for the other.

My first approach was to target only the visible items, but that can't be done via CSS, and I really don't want to go the jQuery road unless absolutely necessary - layout should be done via CSS alone, right?

So, there it is, that's what I can't figure out.

One idea I had was to target 5n+1 and 5n+5 in ranges of 13 items at a time, and then target my pagination links separately, but I can't find the way of doing that, here's what I tried so far:

:nth-child(13n):nth-child(5n+1)
:nth-child(13n+1):nth-child(5n+1)
:nth-child(5n+1):nth-child(13n+1)


I guess my idea can be explained as "target every fifth item starting from item 1, and then every fifth item starting from item 5, and repeat the same calculation in ranges of 13 items (1-13, 14-26, 27-39, and so on)"

Anyone knows how I can accomplish that?

Thanks

** UPDATED WITH THE SOLUTION **

Thanks to @Dekel for pointing me in the right direction.

Here's the final CSS I used to make it work

ul li:not(.psr_paging):nth-child(13n-12),
ul li:not(.psr_paging):nth-child(13n-7),
ul li:not(.psr_paging):nth-child(13n-2) {
border-color: cyan;
margin-left: 0;
}

ul li:not(.psr_paging):nth-child(13n-8),
ul li:not(.psr_paging):nth-child(13n-3) {
border-color: blue;
margin-right: 0;
}

#pager-next {
margin-right: 0;
}


And here's a working sample of the thing working as expected:

On codepen: https://codepen.io/andruxnet/pen/ozvEBW?editors=1111

And here:



var page = 0;
var numThumbs = 13;
var paging = [
{'start': 1, 'end': 13},
{'start': 14, 'end': 26},
{'start': 27, 'end': 39}
];

function refreshThumbs() {
var startIndex = paging[ page ].start - 1;
var stopIndex = paging[ page ].end - 1;

// Show/Hide thumbs
var $thumbsUl = $('ul.thumbs');
$thumbsUl.find('li:not(.psr_paging)').each(function(i) {
var $li = $(this);
if ( i >= startIndex && i <= stopIndex ) {
$li.show();
}
else {
$li.hide();
}
});
$('#pager-prev').insertAfter($('ul.thumbs').find('li:visible:not(.psr_paging):last'));
$('#pager-next').insertAfter($('#pager-prev'));
}

$('#pager-next').on('click', function() {
if ( page < 2 ) {
page++;
refreshThumbs();
}
});

$('#pager-prev').on('click', function() {
if ( page > 0 ) {
page--;
refreshThumbs();
}
});

.thumbs-wrap {
width: 460px;
height: 360px;
display: block;
background-color: black;
padding: 10px;
}

.thumbs-wrap ul.thumbs {
display: block;
overflow: hidden;
margin: 0;
padding: 0;
}

.thumbs-wrap ul.thumbs li {
width: 18%;
height: 74px;
margin: 5px;
float: left;
overflow: hidden;
border: 5px solid #ffffff;
font-size: 30px;
color: #ffffff;
text-align: center;
padding-top: 10px;
}

.thumbs-wrap li.thumb-blank {
background-color: red;
}

.thumbs-wrap li.psr_paging a {
font-size: 30px;
text-align: center;
color: #ffffff;
margin: 0 auto;
}

.thumbs-wrap li.psr_paging a:hover {
text-decoration: none;
}

ul li:not(.psr_paging):nth-child(13n-12),
ul li:not(.psr_paging):nth-child(13n-7),
ul li:not(.psr_paging):nth-child(13n-2) {
border-color: cyan;
margin-left: 0;
}

ul li:not(.psr_paging):nth-child(13n-8),
ul li:not(.psr_paging):nth-child(13n-3) {
border-color: blue;
margin-right: 0;
}

#pager-next {
margin-right: 0;
}

<link href="http://getbootstrap.com/dist/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="thumbs-wrap">
<ul class="thumbs">
<li class="thumb-blank">1</li>
<li class="thumb-blank">2</li>
<li class="thumb-blank">3</li>
<li class="thumb-blank">4</li>
<li class="thumb-blank">5</li>
<li class="thumb-blank">6</li>
<li class="thumb-blank">7</li>
<li class="thumb-blank">8</li>
<li class="thumb-blank">9</li>
<li class="thumb-blank">10</li>
<li class="thumb-blank">11</li>
<li class="thumb-blank">12</li>
<li class="thumb-blank">13</li>
<li id="pager-prev" class="psr_paging">
<a class="thumb pageLink prev" href="javascript:void(0);">
<span class="glyphicon glyphicon-menu-left"></span>
</a>
</li>
<li id="pager-next" class="psr_paging">
<a class="thumb pageLink next" href="javascript:void(0);">
<span class="glyphicon glyphicon-menu-right"></span>
</a>
</li>
<li class="thumb-blank" style="display: none;">14</li>
<li class="thumb-blank" style="display: none;">15</li>
<li class="thumb-blank" style="display: none;">16</li>
<li class="thumb-blank" style="display: none;">17</li>
<li class="thumb-blank" style="display: none;">18</li>
<li class="thumb-blank" style="display: none;">19</li>
<li class="thumb-blank" style="display: none;">20</li>
<li class="thumb-blank" style="display: none;">21</li>
<li class="thumb-blank" style="display: none;">22</li>
<li class="thumb-blank" style="display: none;">23</li>
<li class="thumb-blank" style="display: none;">24</li>
<li class="thumb-blank" style="display: none;">25</li>
<li class="thumb-blank" style="display: none;">26</li>
<li class="thumb-blank" style="display: none;">27</li>
<li class="thumb-blank" style="display: none;">28</li>
<li class="thumb-blank" style="display: none;">29</li>
<li class="thumb-blank" style="display: none;">30</li>
<li class="thumb-blank" style="display: none;">31</li>
<li class="thumb-blank" style="display: none;">32</li>
<li class="thumb-blank" style="display: none;">33</li>
<li class="thumb-blank" style="display: none;">34</li>
<li class="thumb-blank" style="display: none;">35</li>
<li class="thumb-blank" style="display: none;">36</li>
<li class="thumb-blank" style="display: none;">37</li>
<li class="thumb-blank" style="display: none;">38</li>
<li class="thumb-blank" style="display: none;">39</li>
</ul>
</div>




Answer

Here is a solution with pure CSS, using :nth-child

Each "block" of li elements you have contains 13 elements, so you can use the 13n+X in the :nth-child selector.
Now what you need to do is subtract the number of li elements from the 13n you got. The first one will be 13n-12 (to get 1), the second will be 13n-7 (to get 6) and the last will be 13n-2 (to get 11).

Here is the working version:

ul {
  width: 115px;
  margin: 0;
  padding: 0;
  list-style: none;
}
ul li {
  margin: 0;
  padding: 0;
  width: 20px;
  height: 20px;
  float: left;
  border: 1px solid black;
  list-style: none;
}
ul li:nth-child(13n-12),
ul li:nth-child(13n-7),
ul li:nth-child(13n-2) {
   background: red;
}
<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>
  <li style="clear: left">14</li><li>15</li><li>16</li><li>17</li> <li>18</li> <li>19</li> <li>20</li> <li>21</li> <li>22</li> <li>23</li> <li>24</li> <li>25</li> <li>26</li>
  <li style="clear: left">27</li> <li>28</li> <li>29</li> <li>30</li> <li>31</li> <li>32</li> <li>33</li> <li>34</li> <li>35</li> <li>36</li> <li>37</li> <li>38</li> <li>39</li>
</ul>