Select odd even child excluding the hidden child

Line 3 is a hidden <div> . I don't want that one to be taken from the odd/even css rule.

enter image description here

What is the best approach to get this to work?

.hidden {display:none;}
.box:not(.hidden):nth-child(odd)  { background: orange; }
.box:not(.hidden):nth-child(even) { background: green;  }
<div class="wrap">
    <div class="box">1</div>
    <div class="box">2</div>
    <div class="box hidden">3</div>
    <div class="box">4</div>
    <div class="box">5</div>
    <div class="box">6</div>
    <div class="box">7</div>
</div>

http://jsfiddle.net/k0wzoweh/

Note: There can be multiple hidden elements.


Solution 1:

:nth-child() pseudo-class looks through the children tree of the parent to match the valid child (odd, even, etc), therefore when you combine it with :not(.hidden) it won't filter the elements properly.

Alternatively, we could fake the effect by CSS gradient as follows:

.hidden {display:none;}

.wrap {
  line-height: 1.2em;
  
  background-color: orange; 
  background-image: linear-gradient(transparent 50%, green 50%);
  background-size: 100% 2.4em;
}
<div class="wrap">
  <div class="box">xx</div>
  <div class="box">xx</div>
  <div class="box hidden">xx</div>
  <div class="box">xx</div>
  <div class="box">xx</div>
  <div class="box">xx</div>
  <div class="box">xx</div>
</div>

Solution 2:

Pseudo-selectors don't stack, so your :not doesn't affect the :nth-child (nor would it affect :nth-of-type etc.

If you can resort to jQuery, you can use the :visible pseudo-selector there, although that's not a part of the CSS spec.

If you're generating the HTML and can change that, you can apply odd/even with logic at run-time, eg in PHP:

foreach ($divs AS $i => $div) {
    echo '<div class="box ' . ($i % 2 ? 'even' : 'odd') . '">x</div>';
}

Even trying to do something tricky like

.box[class='box']:nth-of-type(even)

doesn't work, because the psuedo-selector doesn't even stack onto the attribute selector.

I'm not sure there's any way to do this purely with CSS - I can't think of any right now.

Solution 3:

Here's a CSS-only solution:

.box {
  background: orange;
}

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

.box.hidden {
  display: none;
}

.box.hidden ~ .box:nth-child(odd) {
  background: green;
}

.box.hidden ~ .box:nth-child(even) {
  background: orange;
}
<div class="wrap">
  <div class="box">xx</div>
  <div class="box">xx</div>
  <div class="box hidden">xx</div>
  <div class="box">xx</div>
  <div class="box">xx</div>
  <div class="box">xx</div>
  <div class="box">xx</div>
</div>

Solution 4:

Since my rows are being hidden with js, I found that the easiest approach for me was to just add an additional hidden row after each real row that I hide, and remove the hidden rows when I show the real rows again.

Solution 5:

Hide the rows you want to hide calling .hide() for each table row, then call

$("tr:visible:even").css( "background-color", "" ); // clear attribute for all rows

$("tr:visible:even").css( "background-color", "#ddddff" ); // set attribute for even rows

Add your table name to the selector to be more specific. Using :even makes it skip the Header row.