How to Construct a Table Header Than Spans Multiple Rows in HTML

How can I construct a table header than spans multiple rows in HTML?

This is how I would do it (working fiddle at http://jsfiddle.net/7pDqb/) Tested in Chrome.

th, td { border: 1px solid black }
<table>  <thead>    <tr>      <th colspan="2">Major 1</th>      <th colspan="2">Major 2</th>    </tr>    <tr>      <th>col1</th>      <th>col2</th>      <th>col3</th>      <th>col4</th>    </tr>  </thead>  <tbody>    <tr>      <td>data1</td>      <td>data2</td>      <td>data3</td>      <td>data4</td>    </tr>  </tbody></table>

How can I construct a table header than contains multiple rows in column?

You can use the rowspan attribute:

th {  border:1px solid #000;  height: 20px;  width: 150px;}
    <table>  <thead>    <tr>      <th rowspan="4">#</th>      <th>Name</th>      <th rowspan="2">Permanent Address</th>      <th rowspan="2">Type of Job</th>      <th rowspan="2">Start work</th>    </tr>    <tr>      <th>ID</th>    </tr>    <tr>      <th>M/F</th>      <th rowspan="2">Contract</th>      <th rowspan="2">Place of Work</th>      <th rowspan="2">Work Stops</th>    </tr>    <tr>      <th>Birth</th>    </tr>  </thead></table>

How can I span multiple rows with my table row headings?

You'd just use rowspan, but obviously you'd also need to remove every second heading. You'd then add the new cells for "ROW TITLE" and expand colspan of the empty cell.

.wrapper {
font-family: sans-serif;
text-align: center;
max-width: 100%;
overflow-x: scroll;
}

.table-title {
border: 1px solid black;
padding-left: 20px;
background-color: #eceeef;
}

table {
width: 100%;
}

table thead tr:first-child th:not(:first-child) {
padding: 10px;
}

table thead tr th {
padding: 7px;
}

table tbody tr:nth-child(even) {
background-color: #cfd7e5;
}

table tbody tr:nth-child(even) td:first-child {
background-color: #fff;
}

table tbody tr td {
padding: 10px;
}

table th,
table td {
border: 1px solid black;
}
<div>
<div class="table-title">
<h2>Table title here</h2>
</div>
<table style="table-layout: auto;">
<colgroup></colgroup>
<thead>
<tr>
<th rowspan="4" colspan="2"></th>
<th colspan="3">Merged header title will go here and centred 1</th>
<th colspan="3">Merged header title will go here and centred 2</th>
</tr>
<tr>
<th>COLUMN TITLE 1</th>
<th>COLUMN TITLE 2</th>
<th>COLUMN TITLE 3</th>
<th>COLUMN TITLE 4</th>
<th>COLUMN TITLE 5</th>
<th>COLUMN TITLE 6</th>
</tr>
</thead>
<tbody>
<tr>
<td rowspan="2">Merged header title will go here and centred vertically</td>
<td>ROW TITLE</td>
<td>1st January 2020</td>
<td>1st January 2020</td>
<td>1st January 2020</td>
<td>1st January 2020</td>
<td>1st January 2020</td>
<td>1st January 2020</td>
</tr>
<tr>
<td>ROW TITLE</td>
<td>8th January 2020</td>
<td>8th January 2020</td>
<td>8th January 2020</td>
<td>8th January 2020</td>
<td>8th January 2020</td>
<td>8th January 2020</td>
</tr>
<tr>
<td rowspan="2">Merged header title will go here and centred vertically</td>
<td>ROW TITLE</td>
<td>12th January 2020</td>
<td>12th January 2020</td>
<td>12th January 2020</td>
<td>12th January 2020</td>
<td>12th January 2020</td>
<td>12th January 2020</td>
</tr>
<tr>
<td>ROW TITLE</td>
<td>21st January 2020</td>
<td>21st January 2020</td>
<td>21st January 2020</td>
<td>21st January 2020</td>
<td>21st January 2020</td>
<td>21st January 2020</td>
</tr>
</tbody>
</table>
</div>

What is the best way to code HTML table with multiple headers in between rows?

You'll need to use the ARIA role="rowgroup" attribute on separate <tbody> elements to specify the group of rows to which the header row must apply. Then you should also apply the scope="rowgroup" attribute to your individual header rows.

For example:

<table style="width: 100%;">
<thead>
<tr>
<td> </td>
<th>1</th>
<th>2</th>
<th>3</th>
<th>4</th>
<th>5</th>
<th>6</th>
<th>7</th>
<th>8</th>
<th>9</th>
</tr>
</thead>
<tbody role="rowgroup">
<tr>
<th colspan="10" scope="rowgroup" style="background-color: #e0e0e0; text-align: left;">Header1</th>
</tr>
<tr>
<th>dffddf</th>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td> </td>
<td> </td>
<td>✓</td>
<td>✓</td>
<td>✓</td>
</tr>
<tr>
<th>fdfdfdf</th>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td>✓</td>
</tr>
<tr>
<th>dffddf</th>
<td> </td>
<td> </td>
<td> </td>
<td> </td>
<td> </td>
<td>✓</td>
<td> </td>
<td> </td>
<td> </td>
</tr>
</tbody>
<tbody role="rowgroup">
<tr>
<th colspan="10" scope="rowgroup" style=" background-color: #e0e0e0; text-align: left;">Header2</th>
</tr>
<tr>
<th>5455445</th>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td> </td>
<td> </td>
<td>✓</td>
<td>✓</td>
<td>✓</td>
</tr>
<tr>
<th>fdfggfgf</th>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td>✓</td>
</tr>
</tbody>
<tbody role="rowgroup">
<tr>
<th colspan="10" scope="rowgroup" style=" background-color: #e0e0e0; text-align: left;">Header3</th>
</tr>
<tr>
<th>fgggf</th>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td> </td>
<td>✓</td>
</tr>
<tr>
<th>fgggfgf</th>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td> </td>
<td> </td>
<td>✓</td>
<td> </td>
</tr>
</tbody>
</table>

I tested this code in Chrome 99 with JAWS 2022 and NVDA 2021 to make sure that the screen reader wasn't announcing all row headers each time I moved from column to column.

Here are some useful articles explaining how to implement the ARIA rowgroup role:

W3C Techniques for WCAG 2.1: Using the scope attribute to associate header cells and data cells in data tables

MDN Web Docs: ARIA: rowgroup role

How to Fixed Header in html table with have multiple row header with row-span and coll-span

Add this <div> around your table:

<div class="tableFixHead"></div>

Add this CSS:

.tableFixHead { overflow-y: auto; height: 400px; }

And this jQuery:

var $th = $('.tableFixHead').find('thead th')
$('.tableFixHead').on('scroll', function() {
$th.css('transform', 'translateY('+ this.scrollTop +'px)');
});

JSFiddle: https://jsfiddle.net/mvoisard/unLx7h1z/8/

HTML table on which one TH has 2 rows

I think you don't make any effort to achieve it. As simple as colspan and rowspan. Try to read better next time, the example shared with you in comments is fine.

<table border="1">  <tr>    <th colspan="2">person</th>    <th rowspan="2">amount</th>    <th rowspan="2">total</th>  </tr>  <tr>    <td>name</td>    <td>age</td>  </tr></table>

How to create fixed table header rows that include colspans?

You can have the header row(s) in a separate table to the data rows using the same colgroup settings in both.

The below works fine in IE9, FF14.01 and Chrome 20.0.1132.57.

<table border="1">
<colgroup>
<td width="100px">Column 1</td>
<td width="100px">Column 2</td>
<td width="100px">Column 3</td>
<td width="16px" style="background-color: gray;"><td>
</colgroup>
</table>
<div style="position: absolute; height:75px; overflow-y:scroll; overflow-x:auto">
<table border="1">
<colgroup>
<td width="100px"></td>
<td width="100px"></td>
<td width="100px"></td>
</colgroup>
<tbody>
<tr>
<td>Row 1 - Cell 1</td>
<td>Row 1 - Cell 2</td>
<td>Row 1 - Cell 3</td>
</tr>
<tr>
// rest omitted, see DEMO for full table
</tr>
</tbody>
</table>
</div>

​See DEMO

Edit -- August 3rd 2012

The only way I was able to get it to work was with a bit of trickery alright.
I separated the header as in the first example. But due to the extremly custom multiple different widths in the header, the most reliable way, keeping everything aligned, was to copy the th rows also into the second table but without text inside. That made them "invisible" but forced the columns in the second table to align as expected.

See DEMO

It feels a bit hackish and I'm sure there is a proper solution but it seems to work well in the meantime.

Edit -- August 7th 2012

Is there any way to "box" this entire table construct up in up 100% of
the screen width and change the height for height:150px; to something
more dynamic, like $(window).height()-200

There surely is a more elegant way of doing it but I was able to make the table more dynamic.

The width was less of an issue as I capped the toal width at about 95% and hard-capped the table(s) at a min-width: 600px; for the header and a min-width: 584px for the body, thus ensuring the table stays aligned at all times.

For the dynamic height I used jQuery, binding a resize function to the windows resize event:

$(document).ready(function() {
resizeTableHeight();

$(window).on("resize.resizeTableHeight", function() {
resizeTableHeight();
});
});

function resizeTableHeight() {
var headerHeight = $("#tableHeaderContainer").height();
var documentHeight = $(document).height();
var spacingHeight = 50;

$("#tableBodyContainer").height(documentHeight - headerHeight - spacingHeight);
}​

When you open the fiddle the original height of the fiddle window will most likely be to high to see the dynamics. Just move the divider and shrink the view to see the re-sizing at work.

Do not forget to unbind that event when you are not showing the grid as it still will execute on resize every time.

See dynamic-grid DEMO

You will notice some styles are in the css (top-right) in the fiddle while others are not. My CSS is not very strong and some styles when I moved them from the elements into the CSS area started to be irgnored. I moved into CSS what I could and left the rest hard-coded not to break it. I'm sure someone working with CSS everyday will be able to sort that out for you.

I also added some ids to some elements for CSS and for the jQuery as required.

I suppose for the CSS it can use classes instead. I leave that to you.

Summary
I'm sure there is a way more elegant way to achieve what you want and probably some script-wiz got a plugin for it. Until then this seems to work. What could also happen is that the columns start miss-aligning close to 600 pixels again if a lot of long data is entered into the columns but as stated, this is a very personalised solution and you may need to add some dynamic calculations for some widths with jQuery over time.

Edit -- August 9th 2012

Regarding setting the width of a td I mentioned in the comments. I fixed the issue with the long text in the first column using the classes I mentioned. Works in IE, FF and Chrome.

See DEMO

I used the logic mentioned in the comments. You might find a much better naming convention. I simply used the main-column+ a on/off switch per sub-column. this works out as follows for column 1 styles:

.col01-000{
width: 0px;
}
.col01-001{
width: 75px;
}
.col01-010, .col01-100{
width: 50px;
}
.col01-011, .col01-101{
width: 125px;
}
.col01-110{
width: 100px;
}

I have assigned the first td a class of col01-100 which means 50px.

The second td has now a class of col01-011, indicating this td still belongs into the first main column but has a width of sub-column 2 (50px) and 3(75px). This adds up to 125px.

I hope this makes sense but if not I gladly continue the discussion in a chat and work the measurements out with you if that is something you want to apply.

So far I can see the measurements to be:

Col01

  • Col1 = colspan 3 over 175px
  • 3 Sub-columns breaking 175px down as follows: 50px-50px-75px

Col02

  • Col2 = colspan 3 over 100px
  • 3 Sub-columns breaking 100px down as follows: 40px-30px-30px

Col03

  • Col3 = colspan 1 over 100px
  • No sub-columns

Col04

  • Col4 = colspan 5 over 150px
  • 5 sub-columns breaking 150px down as follows: 30px-30px-30px-30px-30px

Col05

  • Col5 = colspan 1 over 100px
  • No sub-columns


Related Topics



Leave a reply



Submit