Arash Howaida
Arash Howaida

Reputation: 2617

Alternating Table Row Color Robust to display:none Settings

I have a dynamic table that I made with javascript. Depending on different user events, some rows will be hidden, rearranged, ect. To be more specific, I'm using display:none; to do this. The issue is the rows always keep their original background color (imagine if all the rows were visible, then you could see the alternating colors). That would be fine if I had the entire table visible, but like I mentioned, sometimes certain rows will be hidden or appear at different positions. This often results in two or more rows of the same color being stacked on top of each other.

There is a similar post:

Alternate table row color even if row is removed

I tried as many of those solutions as I could. However my problem persists. Probably due to the following reasons:

My code is:

tr:nth-child(even) {
    background:gray;
}
tr:nth-child(odd) {
    background:lightgray;
}

I have tried tr:nth-of-type(odd) and many similar variants. Is there anything else in CSS or native javascript I can try?

More on Visbility/Selection:

CSS:

tr:not(.selected) {
  display: none;
}

JS:

my_circles.each(function(d,i) {
    if (my_bool===true) {
        d3.select(this).classed('selected',true);
        tableRows.get(this).classed("selected", true);
    }
});

I'm using d3.js, but I think I will omit the d3 tag, because this seems more of a css or js issue. This is a small snippet, mostly for context, but essentially we should be able to infer the visibility is toggled by a class assignment. If you are curious, it is whenever the user selects a circle on my adjacent scatter plot.

Upvotes: 3

Views: 1977

Answers (4)

Abhitalks
Abhitalks

Reputation: 28437

Unfortunately, there is no straight-forward CSS only solution for this problem. Primarily because the :not selector does not go together with nth-... selectors.

Your best bet would be to re-stripe your rows everytime via Javascript.

Stripe your rows as soon as your page is loaded. After that, whenever you change display on any row, you fire your stripe function again.

Here is a crude example:

var tab = document.getElementById("tab"), 
  btns = tab.getElementsByTagName("a"), 
  show = document.getElementById("show"), 
  rows
;

stripe(); // Stripe the rows in beginning

// The stripe function itself
function stripe() {
  // select all rows which are not hidden
  rows = tab.querySelectorAll('tr:not(.hidden)');

  // add a class for every second such row
  for(var x = 0; x < rows.length; x++) {
    if (x % 2 == 0) { rows[x].classList.add('alt'); }
    else { rows[x].classList.remove('alt'); }
  }
}

// dummy buttons to hide each row in this demo
[].forEach.call(btns, function(elem) {
  elem.addEventListener('click', hide);
});

// your actual code where you hide your rows
function hide(e) {
  e.target.parentNode.parentNode.classList.add('hidden');
  stripe(); // fire re-striping when hiding rows
}

// dummy button to show rows in this demo
show.addEventListener('click', function(e) {
  rows = tab.querySelectorAll('tr.hidden');
  [].forEach.call(rows, function(row) {
    row.classList.remove('hidden');
  });
  stripe(); // fire re-striping when showing rows
});
table { width: 70%; border: 1px solid gray; border-collapse: collapse; }
td { border: 1px solid gray; padding: 4px; }
tr.hidden { display: none; }
#tab tr.alt { background-color: #ddd;}
<table id="tab"><tbody>
  <tr><td>Row 1</td><td><a href="#">Hide</a></td></tr>
  <tr><td>Row 2</td><td><a href="#">Hide</a></td></tr>
  <tr><td>Row 3</td><td><a href="#">Hide</a></td></tr>
  <tr><td>Row 4</td><td><a href="#">Hide</a></td></tr>
  <tr><td>Row 5</td><td><a href="#">Hide</a></td></tr>
</tbody></table><br />
<a id="show" href="#">Show All</a>

Accompanying fiddle: https://jsfiddle.net/abhitalks/dz5aq5fk/

.

Upvotes: 3

haxxxton
haxxxton

Reputation: 6442

As you correctly pointed out, the reason that the css alternating stripes dont work is that your rows are remaining in place, and just being hidden using display:none.

The trick is to "group" the visible and hidden rows together so that we dont end up with un-event striping. Given that the order of your rows is not important, what we can do is move the hidden rows to either the top (using .insertBefore) or bottom (using .appendChild) of their containing parent. Something similar to this:

my_circles.each(function(d,i) {
    if (my_bool===true) {
        d3.select(this).classed('selected',true);
        var row = tableRows.get(this);
        row.parentNode.appendChild(row);
        row.classed("selected", true);
    }
});

Upvotes: 1

AG_
AG_

Reputation: 2699

this is not a perfect solution, but you can use gradient background in table to get desired result.

below is sample using gradient background in table.

tr:not(.selected) {
  display: none;
}

table {
  background-color: gray; 
  background-image: linear-gradient(transparent 50%, lightgray 50%);
  background-size: 100% 36px;
}
<table width="500" cellpadding="0" cellspacing="0">
  <tr  class="selected">
    <td>A</td>
    <td>B</td>
  </tr>
    <tr  class="selected">
    <td>C</td>
    <td>D</td>
  </tr>
    <tr>
    <td>E</td>
    <td>F</td>
  </tr>
    <tr class="selected">
    <td>G</td>
    <td>H</td>
  </tr>
    <tr>
    <td>I</td>
    <td>J</td>
  </tr>
      <tr>
    <td>E</td>
    <td>F</td>
  </tr>
    <tr class="selected">
    <td>G</td>
    <td>H</td>
  </tr>
  <tr class="selected">
    <td>G</td>
    <td>H</td>
  </tr>
  <tr class="selected">
    <td>G</td>
    <td>H</td>
  </tr>
    <tr>
    <td>I</td>
    <td>J</td>
  </tr>
  
</table>

Upvotes: 1

Tormi Reinson
Tormi Reinson

Reputation: 555

It is not a CSS or native JS solution but here is a d3 based solution. You could change classes of the rows every time the rows in your table change.

d3.selectAll("tr.selected").classed("grey",function(d,i){return i%2 == 0});

It adds the grey class to every second row and removes it from all the rest. Then you can color rows using css.

tr.grey {
    background:gray;
}
tr:not(.grey) {
    background:lightgray;
}

Here is a jsbin that shows this strategy in action.

Upvotes: 2

Related Questions