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
CSS Grids: Align Square Cells with Container Edges with Consistent Grid Gap
Best Practice for Using Svg Images
Share Style Across Web Components "Of The Same Type"
Flex Items Not Centering Vertically
How to Get The Parent Url from an Iframe's Content
CSS How to Make <Td> a Fixed Height
Overlap Border of Parent with H2 Margin Negative
Image Overlay on Responsive Sized Images Bootstrap
Safari - Videos Load Far Too Slowly
How to Center a <Ul> with Left-Aligned <Li>S
Display Simple HTML in a Native Blackberry Application
Show My Location on Google Maps API V3
How to Put Text Over an Image Without Absolute Positioning or Setting The Image as Backbround