Witaj na moim blogu poświęconym programowaniu w HTML, CSS i JavaScript! Dziś zanurzymy się w szczegóły elementu <tbody>, który jest kluczowym elementem struktury tabel w HTML. Jego świadome użycie poprawia semantykę, ułatwia stylizację i usprawnia zachowanie tabel w druku oraz przy dużych zbiorach danych.
<tbody> reprezentuje główną treść tabeli, grupując wiersze danych w czytelną sekcję. W połączeniu z <thead> (nagłówek) i <tfoot> (stopka) tworzy kompletną, semantyczną strukturę tabeli.
Czym dokładnie jest <tbody> i dlaczego warto go używać?
Element <tbody> (table body) służy do grupowania wierszy <tr> zawierających główne dane tabeli. Jeśli umieścisz wiersze bezpośrednio wewnątrz <table>, przeglądarka i tak utworzy domyślną sekcję <tbody> i przeniesie do niej wiersze. Jawne stosowanie <tbody> daje jednak pełną kontrolę nad selektorami CSS i logiką JS.
Najważniejsze korzyści z używania <tbody> są następujące:
- semantyka – przeglądarki, wyszukiwarki i czytniki ekranu lepiej rozumieją strukturę tabeli;
- stylizacja CSS – możesz niezależnie formatować ciało tabeli (np. tła, czcionki, efekty hover) bez modyfikowania nagłówków;
- funkcjonalność – łatwiej uzyskać przewijanie tylko ciała tabeli, powtarzanie nagłówków przy druku czy sortowanie danych;
- elastyczność – możesz mieć wiele sekcji
<tbody>, aby tematycznie grupować wiersze.
Zgodnie ze specyfikacją HTML, <tbody> jest potomkiem <table> (po <caption>, <colgroup> i opcjonalnym <thead>). Wiersze <tr> powinny znajdować się w sekcjach <thead>, <tbody> lub <tfoot>; w przeciwnym razie przeglądarka utworzy sekcję <tbody> implicite.
Elementy potomne – dowolna liczba <tr> oraz elementy wspierające skrypty.
Interfejs DOM – HTMLTableSectionElement (np. document.querySelector('tbody').insertRow()).
<tbody> nie ma atrybutów obowiązkowych; obsługuje atrybuty globalne (np. id, class) i zdarzeniowe (np. onclick). Atrybuty prezentacyjne (np. align, valign) są przestarzałe – używaj CSS.
Podstawowy przykład użycia <tbody>
Oto prosta tabela z pełną, semantyczną strukturą: nagłówek w <thead>, dane w <tbody>, podsumowanie w <tfoot>:
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<title>Przykład tabeli z tbody</title>
<style>
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
thead { background-color: #f2f2f2; }
tbody { background-color: #fafafa; }
tfoot { background-color: #e9e9e9; font-weight: bold; }
</style>
</head>
<body>
<table>
<thead>
<tr>
<th>Miesiąc</th>
<th>Oszczędności</th>
</tr>
</thead>
<tbody>
<tr>
<td>Styczeń</td>
<td>100 zł</td>
</tr>
<tr>
<td>Luty</td>
<td>80 zł</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>Suma</td>
<td>180 zł</td>
</tr>
</tfoot>
</table>
</body>
</html>
Efekt – czytelna tabela z wyróżnionym nagłówkiem oraz odmiennym tłem ciała i stopki.
Zaawansowana stylizacja <tbody> z CSS
Domyślnie <thead>, <tbody> i <tfoot> nie zmieniają układu, ale CSS daje pełną kontrolę. Oto wybrane techniki:
1. Wyrównanie treści w <tbody>
Aby wyrównać zawartość komórek w poziomie i pionie, użyj:
tbody {
text-align: right; /* wyrównanie poziome do prawej */
vertical-align: bottom; /* wyrównanie pionowe do dołu */
}
W tabeli z wysokimi wierszami (np. height: 100px) dane będą „przyklejone” do dołu komórek.
2. Efekty hover tylko w ciele tabeli
Jeśli chcesz, by efekt najechania działał wyłącznie na wiersze z danymi, użyj selektora ograniczonego do <tbody>:
tbody tr:hover {
background-color: #f5f5f5;
cursor: pointer;
}
To pozwala zachować nagłówek w niezmienionej formie.
3. Przewijalne ciało tabeli ze „sticky” nagłówkiem
Dla bardzo długich tabel możesz ustawić przewijanie tylko dla sekcji danych oraz przyklejony nagłówek:
table {
width: 100%;
border-collapse: collapse;
}
thead th {
position: sticky;
top: 0;
background: #f2f2f2;
z-index: 10;
}
tbody {
display: block;
height: 300px; /* stała wysokość ciała tabeli */
overflow-y: auto; /* przewijanie pionowe sekcji danych */
}
thead, tfoot {
display: table;
width: 100%;
table-layout: fixed; /* spójne szerokości kolumn */
}
Uwaga – ta technika bywa wrażliwa na różnice szerokości kolumn; w praktyce często wymaga dodatkowego JS do ich synchronizacji.
<tbody> w praktyce: przykład z danymi sprzedażowymi
Poniżej rozbudowana tabela zakupów z podsumowaniem w stopce i dwoma sekcjami <tbody> (owoce i pieczywo):
<table class="zakupy">
<thead>
<tr>
<th>Produkt</th>
<th>Ilość</th>
<th>Cena (PLN)</th>
<th>Wartość (PLN)</th>
</tr>
</thead>
<tbody class="owoce">
<tr><td>Jabłka</td><td>2 kg</td><td>3.50</td><td>7.00</td></tr>
<tr><td>Mleko</td><td>3 l</td><td>2.80</td><td>8.40</td></tr>
</tbody>
<tbody class="pieczywo">
<tr><td>Chleb</td><td>1 szt.</td><td>4.20</td><td>4.20</td></tr>
</tbody>
<tfoot>
<tr>
<td colspan="3">Suma:</td>
<td>19.60 PLN</td>
</tr>
</tfoot>
</table>
Minimalne style ułatwiają czytelność sekcji w tym przykładzie:
.zakupy {
width: 100%;
border-collapse: collapse;
}
.zakupy th, .zakupy td {
border: 1px solid #ccc;
padding: 12px;
}
.owoce { background-color: #e8f5e8; }
.pieczywo { background-color: #f0f8f0; }
tfoot { background-color: #ddd; font-weight: bold; }
Wiele sekcji <tbody> ułatwia różnicowanie stylów oraz operacje na grupach danych (filtrowanie, ukrywanie, sortowanie).
Integracja z JavaScript: dynamiczne manipulacje <tbody>
Aby dynamicznie dodać wiersz do sekcji danych, możesz użyć interfejsu HTMLTableSectionElement:
const tbody = document.querySelector('tbody');
const nowyWiersz = tbody.insertRow(); // dodaje pusty <tr>
nowyWiersz.insertCell(0).textContent = 'Marzec';
nowyWiersz.insertCell(1).textContent = '120 zł';
Prosty przykład sortowania rosnąco po pierwszej kolumnie (po kliknięciu w pierwszy nagłówek):
const tbody = document.querySelector('tbody');
document.querySelector('thead th').addEventListener('click', () => {
const rows = Array.from(tbody.rows);
rows.sort((a, b) =>
a.cells[0].textContent.localeCompare(b.cells[0].textContent, 'pl')
);
rows.forEach(row => tbody.appendChild(row));
});
Oddzielenie danych w <tbody> upraszcza wybieranie wierszy w JS i minimalizuje ryzyko przypadkowych zmian w nagłówkach.
Najczęstsze błędy i dobre praktyki
Oto praktyki i pułapki, na które warto zwrócić uwagę podczas pracy z tabelami:
- błąd – umieszczanie
<tfoot>po<tbody>; w starszych silnikach przeglądarek powtarzanie stopki przy druku działało pewniej, gdy<tfoot>znajdował się przed<tbody>; - błąd – brak jawnego
<tbody>; przeglądarka doda go implicite, ale tracisz precyzyjną semantykę i łatwe selektory CSS/JS; - praktyka – w większych tabelach (np. ponad 10 wierszy) zawsze wydzielaj
<thead>,<tbody>i (opcjonalnie)<tfoot>; - praktyka – testuj z czytnikami ekranu (NVDA, JAWS), bo poprawna semantyka realnie zwiększa dostępność;
- dla responsywności – stosuj media queries, np.
@media (max-width: 600px) { thead { display: none; } tbody tr { display: block; } }.
Podsumowując korzyści w tabeli porównawczej
Poniższe zestawienie pokazuje różnice między tabelą bez jawnego <tbody> a tabelą z poprawnie zdefiniowanym <tbody>:
| Aspekt | Bez <tbody> | Z <tbody> |
|---|---|---|
| Semantyka | Słaba (implikowana) | Doskonała |
| Stylizacja CSS | Ograniczona selekcja | Precyzyjna (np. tbody tr:hover) |
| Druk/przewijanie | Mała kontrola | Lepsza kontrola (powtarzany nagłówek) |
| Manipulacja w JS | Trudniejsza | Prosta (np. insertRow()) |
Element <tbody> to nie ozdoba – to fundament profesjonalnych, dostępnych i łatwych w utrzymaniu tabel w HTML5.