0

I have a table that has border-collapse: separate in order to have rounded corners.

I need to have a border around the entire table, but without any borders around internal cells.

Without border-collapse: separate a border on the tbody elements works! With separate I can put a top border on th elements, a bottom border on tr:last-child>td elements, a left border on th:first-child,td:first-child and a right border on th:last-child,td:last-child. All this works great. I can even, with lots more code, place the border radii on the appropriate selectors too.

Now here comes the hard part. Sometimes, the last row of the table will have display:none. I need to have the bottom border applied to the last visible row. As I mentioned before, when border-collapse was collapse, a border around <tbody> works great! However, with separate, borders around tbody no longer work.

Is there a way, in CSS, to specify the last non-hidden tr so I can put a border-bottom on it? Or do I have to use JavaScript?

EDIT

I am aware that getting the "last visible element" isn't really possible in general in CSS, however, I would love to know why bordering the tbody fails with border-collapse:separate and if there is a way to get back this cool behavior even with separate.

Ray Toal
  • 86,166
  • 18
  • 182
  • 232
  • Actually, it has a child with colspan=2, which I think _might_ be unique.... Is there a way to get the row _before_ it then? – Ray Toal Aug 25 '17 at 06:27
  • No, I meant the tr itself, not the tds in the tr. – Mr Lister Aug 25 '17 at 06:32
  • I can definitely put a class on it when the table is created. – Ray Toal Aug 25 '17 at 06:33
  • 1
    By the way, I'm confused about your needs. Why not put a border around the table itself? Then it works as expected, whether the last tr is visible or not. – Mr Lister Aug 25 '17 at 06:38
  • I can work with that, actually. The table element actually has left and right padding of 28px, so bordering the table generates the border in the wrong place. The tbody, on the other hand, borders where I want. Perhaps I can re-do the table (produced by a framework) to use margins instead of padding, then it might look good. Thanks. – Ray Toal Aug 25 '17 at 06:53
  • I think the most elegant way, when you can change the HTML anyway, is to put the last tr in a tfoot of its own when it's supposed to be invisible. That way you can still put the border around the tbody. Or around the cells in the tbody, since tr:last-child will still work. – Mr Lister Aug 25 '17 at 06:59
  • Fantastic, this solved everything. Might be worth an answer.... – Ray Toal Aug 25 '17 at 07:08
  • OK. I still wonder though why this is all necessary. If you can change the HTML, why not skip the whole tr? I mean, leave it out completely, rather than trying to have it not rendered? – Mr Lister Aug 25 '17 at 07:10
  • It all started form (1) being required to add rounded corners, (2) having to implement a show/hide on table rows that actually contain nested tables, and (3) being given a multi-column layout that used a margin-right to skip a column and padding on the whole table to "act like" what should have been a margin. I was trying to save existing code. I ended up killing the padding, and using `calc(35% + 28px)` for the right margin. Your KISS-principled question made me just say damn to the framework and do everything the right way from scratch. – Ray Toal Aug 25 '17 at 07:17

1 Answers1

1

If you can change the HTML when the <tr> becomes visible or invisible, I think the most straightforward way is to put it in a <tfoot> whenever it's invisible. Then you can keep the border around the the tbody (or rather, around the cells in the tbody) and make the tfoot invisible.

table, tbody, thead, tfoot, tr {
  border-collapse: separate; border-spacing: 0; border-radius: .5em;
}

tbody tr:first-child td {border-top: 2px outset #777;}

tbody tr:last-child td {border-bottom: 2px outset #777;}

tbody td:first-child {border-left: 2px outset #777;}

tbody td:last-child {border-right: 2px outset #777;}

tbody tr:first-child td:first-child {border-top-left-radius: 0.5em;}

tbody tr:first-child td:last-child {border-top-right-radius: 0.5em;}

tbody tr:last-child td:first-child {border-bottom-left-radius: 0.5em;}

tbody tr:last-child td:last-child {border-bottom-right-radius: 0.5em;}

.invisible {visibility: collapse; display: none;}
<table>
  <caption>This is the table:</caption>
  <tbody>
    <tr>
      <td>this is visible.</td>
      <td>this is visible.</td>
    </tr>
    <tr>
      <td>this is visible.</td>
      <td>this is visible.</td>
    </tr>
  </tbody>
  <tfoot class="invisible">
    <tr>
      <td colspan="3">this is not.</td>
    </tr>
  </tfoot>
</table>

As mentionend in the comments, alternative solutions could be to put the border around the table instead of around the cells (so that you don't have to worry about which rows are visible; you might have to recalculate your margins and paddings though), or to not output the invisible rows at all (either leave them out of the output stream when reloading the page, or remove them from the DOM tree using Javascript).

Mr Lister
  • 45,515
  • 15
  • 108
  • 150