2

I am using nth-child() a lot times – but now i'm confused about the behavior. I have a wrapper element (id=content) and a lot child elements (class=el). When this is the only content in the wrapper everything is fine. When i place a div (without class) before the "el" objects the ordering of nth-child(odd/even) is wrong.

#content {
    background:rgb(200,200,200);
}

.el {
    height:20px;
    background:grey;
    margin-bottom:10px;
}

.el:nth-child(odd) {
    background:green;
}

.el:nth-child(even) {
    background:red;
}

<div id="content">
    <div></div> (or <i>, <span>, ... any element)
    <div class="el">Test 1</div>
    <div class="el">Test 1</div>
    <div class="el">Test 1</div>
    <div class="el">Test 1</div>
    <div class="el">Test 1</div>
    <div class="el">Test 1</div>
</div>

I set up an JSFiddle example for that: https://jsfiddle.net/5p2uh5e5/1/ (first line should be green). If you remove the div before the "el" objects the ordering is right. Even if you place a second object of any type before the "el" objects.

How can i just select the "el"-Divs with odd/even by CSS?

Kevin Lieser
  • 951
  • 1
  • 9
  • 25

4 Answers4

2

Pseudo classes match elements not classes. When you use it with a class the class acts like a filter. So to take your jsFiddle example:

<div id="content">
    <div>xxx</div>
    <div class="el">Test 1</div>
    <div class="el">Test 1</div>
    <div class="el">Test 1</div>
    <div class="el">Test 1</div>
    <div class="el">Test 1</div>
    <div class="el">Test 1</div>
</div>
#content {
    background:rgb(200, 200, 200);
}
.el {
    height:20px;
    background:grey;
    margin-bottom:10px;
}
.el:nth-child(odd) {
    background:green;
}
.el:nth-child(even) {
    background:red;
}

The first div without a class is the first child element of the parent div with class content, and it has the index of one (since pseudo classes start at 1) which is odd. Your .el:nth-child(odd) rule is not applied, because while the child is odd, it doesn't have the class el, so it's not applied. Having the .el on this pseudo class has made it act like a filter.

The next div, the first one with the class el is the second child, its index is two, so it's even, and the .el:nth-child(even) selector is applied and the background is red. And so and and so forth.

j08691
  • 204,283
  • 31
  • 260
  • 272
  • 1
    Okay, thanks. I understand that. Why have i never noticed that?! So there is no plain CSS solution for that - i have to each the Objects with jQuery and mark them via classnames. Is that right? Thanks! – Kevin Lieser Nov 26 '15 at 19:43
  • @KevinLieser There is a solution in CSS if you know how many elements are before the first `el` one. – Mr Lister Nov 26 '15 at 19:44
  • Yeah, thats right - but i do not know how many elements are before. – Kevin Lieser Nov 26 '15 at 19:46
  • Hm, shame... And you also don't know for sure if they are divs or not? If you're certain they're not divs, you can use `nth-of-type`. – Mr Lister Nov 26 '15 at 19:51
  • No. The content comes out of a CMS. Thanks! – Kevin Lieser Nov 26 '15 at 19:57
  • @KevinLieser Then put all the `.el` divs inside a subcontainer of their own. I've updated my own answer, if you care to take a look. – Mr Lister Nov 26 '15 at 19:59
  • Thanks! I'll use the JS solution i've posted because wrapping the elements will cause less freedom in my CMS (the wrapper has also to be created in the CMS - i want to avoid that) – Kevin Lieser Nov 26 '15 at 20:05
2

If you know the number of elements before the first .el one, you can target only the .el ones by writing 2n+x for odd and 2n+y for even, where x is that number plus 1 and y is the number plus 2.

So if you have 2 elements, you get 2n+3 for odd and 2n+4 for even.

#content {
    background:rgb(200,200,200);
}

.el {
    height:20px;
    background:grey;
    margin-bottom:10px;
}

.el:nth-child(2n+3) { /* the number is the amount of unstyled elements before the first .el plus 1 */
    background:green;
}

.el:nth-child(2n+4) { /* and here, the amount plus 2 */
    background:red;
}
<div id="content">
    <div>nothing</div> <!-- note, two elements inserted -->
    <div>nothing</div>
    <div class="el">Test 1</div>
    <div class="el">Test 1</div>
    <div class="el">Test 1</div>
    <div class="el">Test 1</div>
    <div class="el">Test 1</div>
    <div class="el">Test 1</div>
</div>

Edit: if you don't know the number of inserted elements, a possible solution might be to put all the .el divs into a container of their own.

#content {
  background: rgb(200, 200, 200);
}
.el {
  height: 20px;
  background: grey;
  margin-bottom: 10px;
}
/* Note: more specific selector */
#content > div > .el:nth-child(odd) {
  background: green;
}
#content > div > .el:nth-child(even) {
  background: red;
}
<div id="content">
  <div>nothing</div> <!-- note, two elements inserted -->
  <div>nothing</div>
  <div>              <!-- and a new container for the els -->
    <div class="el">Test 1</div>
    <div class="el">Test 1</div>
    <div class="el">Test 1</div>
    <div class="el">Test 1</div>
    <div class="el">Test 1</div>
    <div class="el">Test 1</div>
  </div>
</div>

Edit 2: If that's not possible, one other possibility could be to target each individual .el separately. For instance, :not(.el) + .el targets the first .el, :not(.el) + .el + .el + .el targets the third, etc.

The restriction here is that you must know beforehand how many .el elements there will be at most. In this example, it will work for up to 10. If there may be more, you will have to use more (and longer) selectors.

#content {
    background:rgb(200,200,200);
}

.el {
    height:20px;
    background:grey;
    margin-bottom:10px;
}

#content .el {
  background:red;
}

#content :not(.el) + .el,
#content :not(.el) + .el + .el + .el,
#content :not(.el) + .el + .el + .el + .el + .el,
#content :not(.el) + .el + .el + .el + .el + .el + .el + .el,
#content :not(.el) + .el + .el + .el + .el + .el + .el + .el + .el + .el
{
    background:green;
}
<div id="content">
    <div>nothing</div> <!-- note, two elements inserted -->
    <div>nothing</div>
    <div class="el">Test 1</div>
    <div class="el">Test 1</div>
    <div class="el">Test 1</div>
    <div class="el">Test 1</div>
    <div class="el">Test 1</div>
    <div class="el">Test 1</div>
</div>

And yet another solution could be to start counting at the end, by using nth-last-child.

This one has as a restriction that you need to know if the number of .el divs is odd or even. This example assumes it's even (because there were 6 in the original code); if it's an odd number you will have to change the styles around.

#content {
    background:rgb(200,200,200);
}

.el {
    height:20px;
    background:grey;
    margin-bottom:10px;
}

.el:nth-last-child(even) {
    background:green;
}

.el:nth-last-child(odd) {
    background:red;
}
<div id="content">
    <div>nothing</div> <!-- note, two elements inserted -->
    <div>nothing</div>
    <div class="el">Test 1</div>
    <div class="el">Test 1</div>
    <div class="el">Test 1</div>
    <div class="el">Test 1</div>
    <div class="el">Test 1</div>
    <div class="el">Test 1</div>
</div>
Mr Lister
  • 45,515
  • 15
  • 108
  • 150
  • @KevinLieser I added two more solutions. Imperfect as they are, one of them may suit your needs. (Haven't found a perfect solution yet, sorry.) – Mr Lister Nov 27 '15 at 08:18
0

With plain CSS it will not work just to select the specified class elements with nth-child so i made a small jQuery script for adding odd/even classnames to the elements. In CSS - for sure - i've replaced the nth-child(odd/even) with the classnames .odd/.even. Thanks everybody!

$('.el').each(function(index) {
    if ((index + 1) %2 == 1) { $(this).addClass('odd'); }
    else { $(this).addClass('even'); }
});
Kevin Lieser
  • 951
  • 1
  • 9
  • 25
-3

Well, i found one solution, just reverse the handling, make:

.el:nth-child(even) {
    background:green;
}

.el:nth-child(odd) {
    background:red;
} 

that will solve your problem....

David Stančík
  • 340
  • 6
  • 23
  • 1
    Sorry, for sure i can reverse odd/even but there are more complex layouts out there in the whole world...Your solution also does not solve the problem when objects before changing. – Kevin Lieser Nov 26 '15 at 19:40