Tables & Data
Advanced Tables (colspan, rowspan, caption)
Master advanced table features for complex data layouts. Learn colspan, rowspan, column groups, and techniques for sophisticated table structures.
Spanning Columns (colspan)
colspan makes a cell span across multiple columns.
HTML
<table>
<tr>
<th colspan="3">Quarter 1 Sales Report</th>
</tr>
<tr>
<th>Product</th>
<th>Units</th>
<th>Revenue</th>
</tr>
<tr>
<td>Laptops</td>
<td>150</td>
<td>$150,000</td>
</tr>
<tr>
<td>Tablets</td>
<td>200</td>
<td>$100,000</td>
</tr>
<tr>
<th colspan="2">Total Revenue</th>
<td>$250,000</td>
</tr>
</table>
<!-- The first header spans all 3 columns -->
<!-- The footer label spans 2 columns -->Spanning Rows (rowspan)
rowspan makes a cell span across multiple rows.
HTML
<table>
<tr>
<th>Department</th>
<th>Employee</th>
<th>Role</th>
</tr>
<tr>
<td rowspan="3">Engineering</td>
<td>Alice</td>
<td>Senior Dev</td>
</tr>
<tr>
<!-- Engineering cell spans here -->
<td>Bob</td>
<td>DevOps</td>
</tr>
<tr>
<!-- Engineering cell spans here -->
<td>Carol</td>
<td>QA Lead</td>
</tr>
<tr>
<td rowspan="2">Marketing</td>
<td>Dave</td>
<td>Manager</td>
</tr>
<tr>
<!-- Marketing cell spans here -->
<td>Eve</td>
<td>Designer</td>
</tr>
</table>Combining colspan and rowspan
HTML
<table>
<tr>
<th rowspan="2">Product</th>
<th colspan="2">Q1</th>
<th colspan="2">Q2</th>
</tr>
<tr>
<!-- Product header spans here -->
<th>Units</th>
<th>Revenue</th>
<th>Units</th>
<th>Revenue</th>
</tr>
<tr>
<td>Laptops</td>
<td>100</td>
<td>$100k</td>
<td>120</td>
<td>$120k</td>
</tr>
<tr>
<td>Tablets</td>
<td>80</td>
<td>$40k</td>
<td>90</td>
<td>$45k</td>
</tr>
</table>Column Groups (<colgroup>)
Apply styling to entire columns without repeating styles on each cell.
HTML
<table>
<colgroup>
<col style="background-color: #f0f0f0;">
<col span="2" style="background-color: #e0e0e0;">
<col style="background-color: #d0d0d0;">
</colgroup>
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th>City</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr>
<td>John</td>
<td>28</td>
<td>NYC</td>
<td>Active</td>
</tr>
<tr>
<td>Jane</td>
<td>34</td>
<td>LA</td>
<td>Active</td>
</tr>
</tbody>
</table>
<!-- Benefits: -->
<!-- - Style columns without repeating CSS -->
<!-- - Better semantic structure -->
<!-- - Easier maintenance -->Using Classes with colgroup
HTML
<style>
.highlight {
background-color: #fffacd;
}
.important {
background-color: #ffcccc;
font-weight: bold;
}
</style>
<table>
<colgroup>
<col>
<col class="highlight">
<col class="important">
</colgroup>
<tr>
<th>Product</th>
<th>Price</th>
<th>Stock</th>
</tr>
<tr>
<td>Laptop</td>
<td>$999</td>
<td>5</td>
</tr>
</table>Complex Table Example: Schedule
HTML
<table>
<caption>Weekly Class Schedule</caption>
<colgroup>
<col style="width: 100px;">
<col span="5" style="width: 150px;">
</colgroup>
<thead>
<tr>
<th>Time</th>
<th>Monday</th>
<th>Tuesday</th>
<th>Wednesday</th>
<th>Thursday</th>
<th>Friday</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">9:00</th>
<td>Math</td>
<td rowspan="2">Science Lab</td>
<td>Math</td>
<td>English</td>
<td>Math</td>
</tr>
<tr>
<th scope="row">10:00</th>
<td>English</td>
<!-- Science Lab spans here -->
<td>History</td>
<td>Math</td>
<td>PE</td>
</tr>
<tr>
<th scope="row">11:00</th>
<td colspan="5" style="text-align: center; background: #f0f0f0;">
Lunch Break
</td>
</tr>
<tr>
<th scope="row">12:00</th>
<td>Science</td>
<td>Art</td>
<td>PE</td>
<td>Science</td>
<td>Music</td>
</tr>
</tbody>
</table>Nested Tables (Use Sparingly)
Tables within tables. Generally avoid, but sometimes necessary for complex layouts.
HTML
<table>
<tr>
<th>Employee</th>
<th>Projects</th>
</tr>
<tr>
<td>John Doe</td>
<td>
<!-- Nested table -->
<table>
<tr>
<th>Project</th>
<th>Status</th>
</tr>
<tr>
<td>Website</td>
<td>Active</td>
</tr>
<tr>
<td>App</td>
<td>Complete</td>
</tr>
</table>
</td>
</tr>
</table>
<!-- âš ï¸ Warning: Nested tables are hard to maintain and less accessible -->
<!-- Consider alternative layouts (CSS Grid, Flexbox) -->Sortable Tables (with JavaScript)
HTML
<table id="sortable-table">
<thead>
<tr>
<th onclick="sortTable(0)">Name ↕</th>
<th onclick="sortTable(1)">Age ↕</th>
<th onclick="sortTable(2)">City ↕</th>
</tr>
</thead>
<tbody>
<tr>
<td>John</td>
<td>28</td>
<td>NYC</td>
</tr>
<tr>
<td>Alice</td>
<td>34</td>
<td>LA</td>
</tr>
<tr>
<td>Bob</td>
<td>22</td>
<td>Chicago</td>
</tr>
</tbody>
</table>
<script>
function sortTable(columnIndex) {
const table = document.getElementById('sortable-table');
const tbody = table.querySelector('tbody');
const rows = Array.from(tbody.querySelectorAll('tr'));
// Toggle sort direction
const isAscending = tbody.dataset.sortDir !== 'asc';
tbody.dataset.sortDir = isAscending ? 'asc' : 'desc';
// Sort rows
rows.sort((a, b) => {
const aText = a.cells[columnIndex].textContent;
const bText = b.cells[columnIndex].textContent;
// Try numeric comparison
const aNum = parseFloat(aText);
const bNum = parseFloat(bText);
if (!isNaN(aNum) && !isNaN(bNum)) {
return isAscending ? aNum - bNum : bNum - aNum;
}
// String comparison
return isAscending
? aText.localeCompare(bText)
: bText.localeCompare(aText);
});
// Re-append sorted rows
rows.forEach(row => tbody.appendChild(row));
}
</script>
<style>
th {
cursor: pointer;
user-select: none;
}
th:hover {
background-color: #ddd;
}
</style>Sticky Headers
HTML
<style>
.table-container {
max-height: 400px;
overflow-y: auto;
}
table {
border-collapse: collapse;
width: 100%;
}
thead th {
position: sticky;
top: 0;
background-color: #2c3e50;
color: white;
z-index: 10;
}
tbody td {
padding: 12px;
border: 1px solid #ddd;
}
</style>
<div class="table-container">
<table>
<thead>
<tr>
<th>Column 1</th>
<th>Column 2</th>
<th>Column 3</th>
</tr>
</thead>
<tbody>
<!-- Many rows... header stays visible -->
<tr><td>Data</td><td>Data</td><td>Data</td></tr>
<!-- ... 100 more rows ... -->
</tbody>
</table>
</div>Accessibility for Complex Tables
Using headers attribute
HTML
<table>
<tr>
<th id="name">Name</th>
<th id="q1" colspan="2">Q1</th>
<th id="q2" colspan="2">Q2</th>
</tr>
<tr>
<th id="blank"></th>
<th id="units-q1" headers="q1">Units</th>
<th id="rev-q1" headers="q1">Revenue</th>
<th id="units-q2" headers="q2">Units</th>
<th id="rev-q2" headers="q2">Revenue</th>
</tr>
<tr>
<td headers="name">Product A</td>
<td headers="units-q1 q1">100</td>
<td headers="rev-q1 q1">$10k</td>
<td headers="units-q2 q2">120</td>
<td headers="rev-q2 q2">$12k</td>
</tr>
</table>
<!-- Screen readers announce: -->
<!-- "Product A, Q1 Units: 100" -->
<!-- "Product A, Q1 Revenue: $10k" -->Responsive Advanced Tables
HTML
<style>
/* Desktop: normal table */
table {
border-collapse: collapse;
width: 100%;
}
/* Mobile: card layout */
@media screen and (max-width: 768px) {
table {
border: 0;
}
thead {
display: none;
}
tbody tr {
display: block;
margin-bottom: 20px;
border: 2px solid #ddd;
border-radius: 8px;
padding: 10px;
}
tbody td {
display: block;
text-align: right;
padding: 10px;
border: none;
}
tbody td::before {
content: attr(data-label);
float: left;
font-weight: bold;
}
tbody td[colspan] {
text-align: center;
}
}
</style>
<table>
<thead>
<tr>
<th>Name</th>
<th>Position</th>
<th>Department</th>
</tr>
</thead>
<tbody>
<tr>
<td data-label="Name">John Doe</td>
<td data-label="Position">Developer</td>
<td data-label="Department">Engineering</td>
</tr>
</tbody>
</table>Best Practices
✅ Do
- Use colspan/rowspan for logical data grouping
- Add scope or headers attributes for accessibility
- Use colgroup for column styling
- Test with screen readers
- Ensure mobile responsiveness
- Use caption for table description
- Keep structure simple when possible
⌠Don't
- Overuse colspan/rowspan unnecessarily
- Nest tables (use CSS instead)
- Forget accessibility attributes
- Make tables too complex to understand
- Use tables for layout
- Forget mobile users
- Use empty cells for spacing
Common Pitfalls
1. Miscounting spans
HTML
<!-- ⌠Wrong: Total columns don't match -->
<tr>
<td colspan="2">A</td>
<td>B</td>
<td>C</td> <!-- 4 columns total -->
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td> <!-- Only 3 columns! -->
</tr>2. Forgetting accessibility
HTML
<!-- ⌠Bad: Complex table without headers attribute -->
<td>Value</td>
<!-- ✅ Good: -->
<td headers="q1 units">Value</td>