124

I'm trying to design some HTML/CSS that can put a border around specific rows in a table. Yes, I know I'm not really supposed to use tables for layout but I don't know enough CSS to completely replace it yet.

Anyways, I have a table with multiple rows and columns, some merged with rowspan and colspan, and I'd like to put a simple border around parts of the table. Currently, I'm using 4 separate CSS classes (top, bottom, left, right) that I attach to the <td> cells that are along the top, bottom, left, and right of the table respectively.

.top {
  border-top: thin solid;
  border-color: black;
}

.bottom {
  border-bottom: thin solid;
  border-color: black;
}

.left {
  border-left: thin solid;
  border-color: black;
}

.right {
  border-right: thin solid;
  border-color: black;
}
<html>

<body>

  <table cellspacing="0">
    <tr>
      <td>no border</td>
      <td>no border here either</td>
    </tr>
    <tr>
      <td class="top left">one</td>
      <td class="top right">two</td>
    </tr>
    <tr>
      <td class="bottom left">three</td>
      <td class="bottom right">four</td>
    </tr>
    <tr>
      <td colspan="2">once again no borders</td>
    </tr>
    <tr>
      <td class="top bottom left right" colspan="2">hello</td>
    </tr>
    <tr>
      <td colspan="2">world</td>
    </tr>
  </table>

</html>

Is there any easier way to do what I want? I tried applying top and bottom to a <tr> but it didn't work. (p.s. I'm new to CSS, so there's probably a really basic solution to this that I've missed.)

note: I do need to have multiple bordered sections. The basic idea is to have multiple bordered clusters each containing multiple rows.

Legends
  • 21,202
  • 16
  • 97
  • 123
Kyle Cronin
  • 77,653
  • 43
  • 148
  • 164
  • 9
    Off-topic but I just wanted to say, tables are perfectly appropriate and recommended when displaying tabular data... – md1337 Mar 06 '13 at 18:31

10 Answers10

120

How about tr {outline: thin solid black;}? Works for me on tr or tbody elements, and appears to be compatible with the most browsers, including IE 8+ but not before.

enigment
  • 3,316
  • 7
  • 30
  • 35
  • I was asking about putting a single border around multiple rows in a table, essentially visually dividing it into multiple sections but within the same table so stuff from different sections would align. – Kyle Cronin Jun 05 '13 at 14:31
  • 4
    Understood, I needed that too. Enclose the set of rows you want a border around in their own tbody, and the above css will create a border around the set of them -- i.e., a top border on the top row, a bottom border on the bottom row, and left and right borders on all rows in the tbody. The borders aren't actually "on" those rows, they're on the outline of the tbody itself, just trying to describe the effect. – enigment Jun 07 '13 at 13:46
  • Oh, I see, multiple tbodys - that works, and is something I hadn't considered. Upvoted :) – Kyle Cronin Jun 07 '13 at 15:13
  • I would suggest using the nth-child() css property rather than defining tbodys unless you really have some pretty dynamic data. Using nth-child(), nth-last-child(), and not(), you can select any rows/cells you want (as long as you know the relative indexes of things in the table). For example, you can select all rows except the top two and bottom one with tr:not(:nth-child(-n+2)):not(:nth-last-child(1)) – B T Jul 11 '14 at 19:05
  • 1
    Note that outline does *not* have the possibility to border specific sides of an element. There is no "outline-top" for example. This is a limited solution – B T Jul 11 '14 at 19:35
  • In Microsoft Edge this looks odd. The border position is outside of the line of the cell border - at least in the header row when I tried it –  Sep 21 '18 at 09:35
53

Thank you to all that have responded! I've tried all of the solutions presented here and I've done more searching on the internet for other possible solutions, and I think I've found one that's promising:

tr.top td {
  border-top: thin solid black;
}

tr.bottom td {
  border-bottom: thin solid black;
}

tr.row td:first-child {
  border-left: thin solid black;
}

tr.row td:last-child {
  border-right: thin solid black;
}
<html>

<head>
</head>

<body>

  <table cellspacing="0">
    <tr>
      <td>no border</td>
      <td>no border here either</td>
    </tr>
    <tr class="top row">
      <td>one</td>
      <td>two</td>
    </tr>
    <tr class="bottom row">
      <td>three</td>
      <td>four</td>
    </tr>
    <tr>
      <td colspan="2">once again no borders</td>
    </tr>
    <tr class="top bottom row">
      <td colspan="2">hello</td>
    </tr>
    <tr>
      <td colspan="2">world</td>
    </tr>
  </table>

</body>

</html>

Output:

enter image description here

Instead of having to add the top, bottom, left, and right classes to every <td>, all I have to do is add top row to the top <tr>, bottom row to the bottom <tr>, and row to every <tr> in between. Is there anything wrong with this solution? Are there any cross-platform issues I should be aware of?

Legends
  • 21,202
  • 16
  • 97
  • 123
Kyle Cronin
  • 77,653
  • 43
  • 148
  • 164
  • Just ran a test through browsershots and it looks like IE (all versions) doesn't like the first-child and last-child attributes. :-/ – Kyle Cronin Mar 22 '09 at 05:23
  • 1
    Looks like IE 7 & 8 support first-child, but none support last-child (!) http://msdn.microsoft.com/en-us/library/cc351024(VS.85).aspx – mechanical_meat Mar 22 '09 at 06:08
  • The `cellspacing` attribute is deprecated in HTML5. Seems like CSS [`table { border-collapse: collapse; border-spacing: 0; }`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/table) is the way to go now. – Stefan van den Akker May 05 '14 at 12:46
36

If you set the border-collapse style to collapse on the parent table you should be able to style the tr: (styles are inline for demo)

<table style="border-collapse: collapse;">
  <tr>
    <td>No Border</td>
  </tr>
  <tr style="border:2px solid #f00;">
    <td>Border</td>
  </tr>
  <tr>
    <td>No Border</td>
  </tr>
</table>

Output:

HTML output

Stefan van den Akker
  • 6,661
  • 7
  • 48
  • 63
Sunrise
  • 1,455
  • 2
  • 16
  • 13
9

I was just playing around with doing this too, and this seemed to be the best option for me:

<style>
    tr { 
        display: table;            /* this makes borders/margins work */
        border: 1px solid black;
        margin: 5px;
    }
</style>

Note that this will prevent the use of fluid/automatic column widths, as cells will no longer align with those in other rows, but border/colour formatting still works OK. The solution is to give the TR and TDs a specified width (either px or %).

Of course you could make the selector tr.myClass if you wanted to apply it only to certain rows. Apparently display: table doesn't work for IE 6/7, however, but there's probably other hacks (hasLayout?) that might work for those. :-(

Simon East
  • 55,742
  • 17
  • 139
  • 133
  • 6
    This solution is incorrect: "display: table" puts the whole row into one table cell --- you lose the formatting with respect to the other rows of the table. I tried this in Firefox and Chromium. – Yaakov Belch Feb 13 '13 at 10:44
  • Yaakov, I think what you're referring to is that fluid column widths no longer align with other rows in the table (as seen in this fiddle: http://jsfiddle.net/MrKtw/ ) but border/colour formatting still works OK. The solution is to give the TR and TDs a specified width (either px or %). – Simon East Feb 14 '13 at 00:32
  • Simon, to show what I mean, I forked and changed your fiddle. Look at this: http://jsfiddle.net/a6DZV/ --- I apply the "display: table" formatting to only one row. As you see, this turns this row effectively into one cell of the table. In other words: You get the same output as nesting one-row tables inside a cell of another table (and showing the border of this inner table). Your solution saves a few nodes in the DOM but is not as compatible. – Yaakov Belch Feb 15 '13 at 11:17
3

Here's an approach using tbody elements that could be the way to do it. You can't set the border on a tbody (same as you can't on a tr) but you can set the background colour. If the effect you're wanting to acheive can be obtained with a background colour on the groups of rows instead of a border this will work.

<table cellspacing="0">  
    <tbody>
        <tr>    
            <td>no border</td>    
            <td>no border here either</td>  
        </tr>  
    <tbody bgcolor="gray">
        <tr>    
            <td>one</td>    
            <td>two</td>  
        </tr>  
        <tr>    
            <td>three</td>    
            <td>four</td>  
        </tr>  
    <tbody>
        <tr>    
             <td colspan="2">once again no borders</td>  
        </tr>  
    <tbody bgcolor="gray">
        <tr>    
             <td colspan="2">hello</td>  
        </tr>
    <tbody>
    <tr>    
         <td colspan="2">world</td>  
    </tr>
</table>
sipsorcery
  • 30,273
  • 24
  • 104
  • 155
1

Group rows together using the <tbody> tag and then apply style.

<table>
  <tr><td>No Style here</td></tr>
  <tbody class="red-outline">
    <tr><td>Style me</td></tr>
    <tr><td>And me</td></tr>
  </tbody>
  <tr><td>No Style here</td></tr>
</table>  

And the css in style.css

.red-outline {
  outline: 1px solid red;
}
csi
  • 9,018
  • 8
  • 61
  • 81
1

the trick is with outline property thanks to enigment's answer with little modification

use this class

.row-border{
    outline: thin solid black;
    outline-offset: -1px;
}

then in the HTML

<tr>....</tr>
<tr class="row-border">
    <td>...</td>
    <td>...</td>
</tr>

and the result is enter image description here hope this helps you

Community
  • 1
  • 1
Basheer AL-MOMANI
  • 14,473
  • 9
  • 96
  • 92
1

Based on your requirement that you want to put a border around an arbitrary block of MxN cells there really is no easier way of doing it without using Javascript. If your cells are fixed with you can use floats but this is problematic for other reasons. what you're doing may be tedious but it's fine.

Ok, if you're interested in a Javascript solution, using jQuery (my preferred approach), you end up with this fairly scary piece of code:

<html>
<head>

<style type="text/css">
td.top { border-top: thin solid black; }
td.bottom { border-bottom: thin solid black; }
td.left { border-left: thin solid black; }
td.right { border-right: thin solid black; }
</style>
<script type="text/javascript" src="jquery-1.3.1.js"></script>
<script type="text/javascript">
$(function() {
  box(2, 1, 2, 2);
});

function box(row, col, height, width) {
  if (typeof height == 'undefined') {
    height = 1;
  }
  if (typeof width == 'undefined') {
    width = 1;
  }
  $("table").each(function() {
    $("tr:nth-child(" + row + ")", this).children().slice(col - 1, col + width - 1).addClass("top");
    $("tr:nth-child(" + (row + height - 1) + ")", this).children().slice(col - 1, col + width - 1).addClass("bottom");
    $("tr", this).slice(row - 1, row + height - 1).each(function() {
      $(":nth-child(" + col + ")", this).addClass("left");
      $(":nth-child(" + (col + width - 1) + ")", this).addClass("right");
    });
  });
}
</script>
</head>
<body>

<table cellspacing="0">
  <tr>
    <td>no border</td>
    <td>no border here either</td>
  </tr>
  <tr>
    <td>one</td>
    <td>two</td>
  </tr>
  <tr>
    <td>three</td>
    <td>four</td>
  </tr>
  <tr>
    <td colspan="2">once again no borders</td>
  </tr>
</tfoot>
</table>
</html>

I'll happily take suggestions on easier ways to do this...

cletus
  • 616,129
  • 168
  • 910
  • 942
  • 1
    Seems like you are just adding classes to the td tags? Why couldn't this be done with some server-side script, generated statically, or worst-case just by hand? Looks like JavaScript abuse to me. – Thomas Mar 22 '09 at 04:36
  • 3
    Actually the poster ASKED for a Javascript solution. You can't say it's Javascript abuse since there's not enough information. For example, are the borders being added because of clicking the user does? If so, a server solution is incorrect. – cletus Mar 22 '09 at 04:41
  • Sorry about the lack of information. This will be generated server-side, so it's true that I could just add in the classes manually, but I do like how the JS solution provides a simpler interface to do so. So while I probably won't go with the JS it's a good solution to see. – Kyle Cronin Mar 22 '09 at 04:44
1

The only other way I can think of to do it is to enclose each of the rows you need a border around in a nested table. That will make the border easier to do but will potentially creat other layout issues, you'll have to manually set the width on table cells etc.

Your approach may well be the best one depending on your other layout rerquirements and the suggested approach here is just a possible alternative.

<table cellspacing="0">  
    <tr>    
        <td>no border</td>    
        <td>no border here either</td>  
    </tr>  
    <tr>
        <td>
             <table style="border: thin solid black">
                  <tr>    
                        <td>one</td>    
                        <td>two</td>  
                  </tr>  
                  <tr>    
                      <td>three</td>    
                      <td>four</td>  
                  </tr>  
             </table>
         </td>
    </tr>
    <tr>    
         <td colspan="2">once again no borders</td>  
    </tr>  
    <tr>
        <td>
             <table style="border: thin solid black">
                  <tr>    
                        <td>hello</td>  
                   </tr>
             </table>
         </td>
    </tr>
    <tr>    
         <td colspan="2">world</td>  
    </tr>
</table>
sipsorcery
  • 30,273
  • 24
  • 104
  • 155
  • Thanks for your answer; you're right about the layout issues though - I would prefer columns to line up without doing it by hand. What about applying a class to a tag - is that possible? – Kyle Cronin Mar 22 '09 at 02:41
  • If you use a table with “table-layout: fixed” and explicitly set the width of each column (by using or just setting the width of cells in the first row), the columns will line up regardless of content. You don't even need to nest the tables, three separate tables would do the example fine. – bobince Mar 22 '09 at 02:56
-5

An easier way is to make the table a server side control. You could use something similar to this:

Dim x As Integer
table1.Border = "1"

'Change the first 10 rows to have a black border
 For x = 1 To 10
     table1.Rows(x).BorderColor = "Black"
 Next

'Change the rest of the rows to white
 For x = 11 To 22
     table1.Rows(x).BorderColor = "White"
 Next
ForceMagic
  • 6,230
  • 12
  • 66
  • 88
Curtis
  • 5
  • 1
    OP is asking for a _easier_ way to do it – orique Jul 09 '13 at 22:06
  • 2
    Be carefull, OP is also asking for a easier way to do it in HTML/CSS. I don't see anywhere in his question the VB or VBA keyword. I suggest you have a look to our Help section : http://stackoverflow.com/help/how-to-answer Good Luck. – ForceMagic Jul 09 '13 at 22:13