32

I've got a table

<table id="mytable">
    <tr style="display: none;"><td>&nbsp;</td></tr>
    <tr><td>&nbsp;</td></tr>
    <tr style="display: none;"><td>&nbsp;</td></tr>
    <tr><td>&nbsp;</td></tr>
    <tr><td>&nbsp;</td></tr>
    <tr><td>&nbsp;</td></tr>
 </table>

I'm trying to set the table striping to use nth-child selectors but just can't seem to crack it.

 table #mytable tr[@display=block]:nth-child(odd) { 
      background-color:  #000;  
 }
 table #mytable tr[@display=block]:nth-child(odd) { 
      background-color:  #FFF;
 }

I'm pretty sure I'm close ... can't quite seem to crack it.

anyone pass along a clue?

Alex C
  • 16,624
  • 18
  • 66
  • 98

12 Answers12

20

Here's as close as you're going to get. Note that you can't make the nth-child count only displayed rows; nth-child will take the nth child element no matter what, not the nth child that matches a given selector. If you want some rows to be missing and not affect the zebra-striping, you will have to remove them from the table entirely, either through the DOM or on the server side.

#mytable tr:nth-child(odd) {
  background-color: #000;
}

#mytable tr:nth-child(even) {
  background-color: #FFF;
}
<table id="mytable">
  <tr><td>&nbsp;</td></tr>
  <tr><td>&nbsp;</td></tr>
  <tr><td>&nbsp;</td></tr>
  <tr><td>&nbsp;</td></tr>
  <tr><td>&nbsp;</td></tr>
  <tr><td>&nbsp;</td></tr>
</table>

Here are the fixes that I made:

 table #mytable tr[@display=block]:nth-child(odd) { 
      background-color:  #000;  
 }

There's no need to specify an ancestor selector for an id based selector; there is only ever one element that will match #table, so you're just adding extra code by adding the table in.

 #mytable tr[@display=block]:nth-child(odd) { 
      background-color:  #000;  
 }

Now, [@display=block] would match elements which had an attribute display set to block, such as <tr display=block>. Display isn't a valid HTML attribute; what you seem to be trying to do is to have the selector match on the style of the element, but you can't do that in CSS, since the browser needs to apply the styles from the CSS before it can figure that out, which it's in the process of doing when it's applying this selector. So, you won't be able to select on whether table rows are displayed. Since nth-child can only take the nth child no matter what, not nth with some attribute, we're going to have to give up on this part of the CSS. There is also nth-of-type, which selects the nth child of the same element type, but that's all you can do.

 #mytable tr:nth-child(odd) { 
      background-color:  #000;  
 }

And there you have it.

mplungjan
  • 169,008
  • 28
  • 173
  • 236
Brian Campbell
  • 322,767
  • 57
  • 360
  • 340
  • thanks. Since I'm looping through all the rows in the table using prototype and hiding / unhiding based on a filter condition in one of the cells, would it be possible to add a class to all rows that weren't hidden, and to an NTH-CHILD or NTH-OF-TYPE striping based on that class? tr .visible_row:nth-child(odd) ? – Alex C Sep 23 '10 at 03:15
  • or even just: .visible_row:nth-child(odd) { #000; } – Alex C Sep 23 '10 at 03:17
  • 4
    @Alex `:nth-child` counts *all* children of the parent element, regardless of whether it matches any preceding selectors. Remember, putting two selectors together with no space in between means to match any elements which match the first *and* match the second. So if the second row is invisible, `.visible_row:nth-child(even)` won't match the second row, and you'll see the first and third rows styled in the odd color, even though the second row is hidden. If that's the effect you're going for, it works, but you probably want the zebra stripes to only apply to visible rows, which is impossible. – Brian Campbell Sep 23 '10 at 04:49
  • 1
    In order to make the zebra stripes apply only to visible rows, you'll need to actually remove the invisible ones from the table. If you're using Prototype, you probably want to use `Element.remove()` to remove the elements from the table entirely, and then insert them again later when you need them. – Brian Campbell Sep 23 '10 at 04:53
7

If you are using JQuery to change the visibility of rows you can apply the following function to the table to add an .odd class where appropriate. Call it each time the rows visible is different.

        function updateStriping(jquerySelector){
            $(jquerySelector).each(function(index, row){
                $(row).removeClass('odd');
                if (index%2==1){ //odd row
                    $(row).addClass('odd');
                }
            });
        }

And for the css simply do

table#tableid tr.visible.odd{
    background-color: #EFF3FE;
}
Zak Henry
  • 2,075
  • 2
  • 25
  • 36
3

Problem:

selector:not(.hidden):nth-child(even) selects elements that havent .hidden class and also are even. But we need to select subset of even elements from the set of elements without .hidden class. Put differently in case of selector:not(.hidden):nth-child(even) part :nth-child(even) will ignore :not(.hidden) part and acts independently, but not select even from visible. By default nth-child cant ignore hidden rows, cause these rows are still in the DOM.

Solution 1 (works)

Dont hide the rows of the table, but remove them altogether. Save an array of references to DOM elements of table rows (initial state). And on filter action - cycle through this array, and depending on the filtering conditions, call the append method for the table or remove for the row

instead of

    if (condition) {
      row.classList.add('visible')
    } else {
      row.classList.add('hidden')
    }

do

// partically pseudo code
const rows = Array.from(tbody.querySelectorAll<HTMLTableRowElement>('tr'));
onClickOneOf(filterAction, (event) => {
  // ...some your stuff
  for (const row of rows) {
    if (condition) {
      tbody.appendChild(row);
    } else {
      row.remove();
    }
  }
});

now nth-child will work correctly

Solution 2 (partially supported yet in some browsers but so cool)

Source: https://developer.chrome.com/articles/css-nth-child-of-s/#zebra-striping-revisited

A classic example where :nth-child() is used - when creating zebra-striped table. By default, this can be approached as follows:

tr:nth-child(even) {
  background-color: lightgrey;
}

This works for static tables, but it becomes problematic when you start to dynamically filter the table contents.

To fix this, we can leverage :nth-child(An+B [of S]?) by excluding the hidden rows from the An+B logic:

tr:nth-child(even of :not(.hidden-class or other...)) {
  background-color: lightgrey;
}
2

While you can't Zebra stripe a table with hidden rows using CSS3 you can do it with JavaScript. Here is how:

    var table = document.getElementById("mytable");
    var k = 0;
    for (var j = 0, row; row = table.rows[j]; j++) {
        if (!(row.style.display === "none")) {
            if (k % 2) {
                row.style.backgroundColor = "rgba(242,252,244,0.4)";
            } else {
                row.style.backgroundColor = "rgba(0,0,0,0.0)";
            }
            k++;
        }
    }
JSWilson
  • 1,113
  • 1
  • 11
  • 28
  • `if (row.style.display === "none") continue; row.style.backgroundColor = (k % 2) ? "gray" : "white"; k++` – mplungjan Jul 28 '22 at 09:52
1

For a jquery way, you could use this function which iterates through the rows in your table, checking the visbility of the row and (re)setting a class for visible odd rows.

    function updateStriping(jquerySelector) {
        var count = 0;
        $(jquerySelector).each(function (index, row) {
            $(row).removeClass('odd');
            if ($(row).is(":visible")) {
                if (count % 2 == 1) { //odd row
                    $(row).addClass('odd');
                }
                count++;
            }            
        });
    }

Use css to set a background for odd rows.

#mytable tr.odd { background: rgba(0,0,0,.1); }

Then you can call this zebra-striper whenever by using:

updateStriping("#mytable tr");
Stalzer
  • 104
  • 1
  • 3
  • This worked for me, along with one edit. I needed to specifically inventory what was visible in a different way for my application. Here's what worked for me: ```function updateStriping(jquerySelector){ var visibleItems = []; $(jquerySelector).each(function(){ if( $(this).is(":visible") ) { visibleItems.push(this); } }); $('.odd').removeClass('odd'); $(visibleItems).each(function(index, row){ if (index%2==1){ //odd row $(row).addClass('odd'); } else { // something } }); }``` Sorry about the jumbled code. – Steve C. Dec 13 '16 at 14:59
1

I came up with a sort of solution but it's reliant on the fact that the table can only ever have a maximum number of hidden rows and comes with the downside of requiring 2 additional CSS rules for each possible hidden row. The principle is that, after each hidden row, you switch the background-color of the odd and even rows around.

Here's a quick example with just 3 hidden rows and the necessary CSS for up to 4 of them. You can already see how unwieldy the CSS can become but, still, someone may find some use for it:

table{
  background:#fff;
  border:1px solid #000;
  border-spacing:1px;
  width:100%;
}
td{
  padding:20px;
}
tr:nth-child(odd)>td{
  background:#999;
}
tr:nth-child(even)>td{
  background:#000;
}
tr[data-hidden=true]{
  display:none;
}
tr[data-hidden=true]~tr:nth-child(odd)>td{
  background:#000;
}
tr[data-hidden=true]~tr:nth-child(even)>td{
  background:#999;
}
tr[data-hidden=true]~tr[data-hidden=true]~tr:nth-child(odd)>td{
  background:#999;
}
tr[data-hidden=true]~tr[data-hidden=true]~tr:nth-child(even)>td{
  background:#000;
}
tr[data-hidden=true]~tr[data-hidden=true]~tr[data-hidden=true]~tr:nth-child(odd)>td{
  background:#000;
}
tr[data-hidden=true]~tr[data-hidden=true]~tr[data-hidden=true]~tr:nth-child(even)>td{
  background:#999;
}
tr[data-hidden=true]~tr[data-hidden=true]~tr[data-hidden=true]~tr[data-hidden=true]~tr:nth-child(odd)>td{
  background:#999;
}
tr[data-hidden=true]~tr[data-hidden=true]~tr[data-hidden=true]~tr[data-hidden=true]~tr:nth-child(even)>td{
  background:#000;
}
<table>
  <tbody>
    <tr><td></td><td></td></tr>
    <tr><td></td><td></td></tr>
    <tr data-hidden="true"><td></td><td></td></tr>
    <tr><td></td><td></td></tr>
    <tr><td></td><td></td></tr>
    <tr><td></td><td></td></tr>
    <tr><td></td><td></td></tr>
    <tr data-hidden="true"><td></td><td></td></tr>
    <tr data-hidden="true"><td></td><td></td></tr>
    <tr><td></td><td></td></tr>
    <tr><td></td><td></td></tr>
    <tr><td></td><td></td></tr>
    <tr><td></td><td></td></tr>
    <tr><td></td><td></td></tr>
    <tr><td></td><td></td></tr>
    <tr><td></td><td></td></tr>
  </tbody>
</table>
Shaggy
  • 6,696
  • 2
  • 25
  • 45
1

in jquery ..

var odd = true; 
$('table tr:visible').each(function() {   
  $(this).removeClass('odd even').addClass(odd?'odd':'even'); 
  odd=!odd 
});
commonpike
  • 10,499
  • 4
  • 65
  • 58
0

You can easily fake the zebra stripes if you apply a vertically repeating gradient on the parent table, positioned exactly to match the rows' height (the rows would have to be transparent). That way the table won't care if anything's hidden, it will repeat no matter what.

rale09
  • 19
  • 2
0

If anyone tries to do something like me, where I have alternating hidden and visible rows, you can do this:

.table-striped tbody tr:nth-child(4n + 1) {
background-color: rgba(0,0,0,.05);
}

This will get every 4th element starting with the 1st one, and allows you to maintain striping with hidden rows between each visible row.

0

Here is a 2022 version of a javascript version

let cnt = 0;
document.querySelectorAll("#mytable tbody tr").forEach(tr => {
  cnt += tr.hidden ? 0 : 1;
  tr.classList.toggle("odd",cnt%2===0); 
});
.odd { background-color: grey; }
<table id="mytable">
<thead><tr><th>Num</th></tr></thead>
  <tbody>
    <tr><td>1</td></tr>
    <tr><td>2</td></tr>
    <tr><td>3</td></tr>
    <tr hidden><td></td></tr>
    <tr><td>5</td></tr>
    <tr><td>6</td></tr>
  </tbody>  
</table>
mplungjan
  • 169,008
  • 28
  • 173
  • 236
-2

I add in css:

tr[style="display: table-row;"]:nth-child(even) {
      background-color:  #f3f6fa;  
}

and on create tr add in tag

style="display: table-row;"
Anton
  • 5
  • 1
-2

Jquery codes for zebra color in html table

$("#mytabletr:odd").addClass('oddRow');
$("#mytabletr:even").addClass('evenEven');

And CSS you can do

.oddRow{background:#E3E5E6;color:black}
.evenRow{background:#FFFFFF;color:black}