У таблицы, как и у страницы, может быть своя структура: <thead>, <tbody> и <tfoot> делят строки на шапку, тело и подвал, а <caption> даёт таблице подпись. Все четыре тега необязательны - без них таблица тоже работает, - но с ними разметка становится понятнее и для CSS, и для скринридера, и при печати.
В коде слева <tfoot> написан сразу после подписи - раньше, чем <thead> и <tbody>. Но взгляни направо: строка "Итого" всё равно оказалась внизу таблицы. Браузер сам расставляет группы строк в правильном порядке при отрисовке, независимо от того, в каком порядке они написаны в коде.
Как разделить таблицу: thead, tbody и tfoot
<thead> хранит строку (или строки) с заголовками столбцов, <tbody> - основные данные, а <tfoot> - итоговую строку. На экране браузер всегда показывает их в одном и том же порядке - шапка, тело, подвал, - даже если в коде они написаны иначе: <tfoot> в любом случае окажется внизу. <tbody> в таблице может быть несколько - удобно, если строки логически делятся на несколько групп; а если <tbody> вообще не написать явно, браузер всё равно добавит его сам при отрисовке страницы - разметка от этого не станет невалидной. А вот <thead> и <tfoot> в таблице допускаются только по одному разу каждый.
<tbody>...</tbody><tbody>...</tbody> - два блока tbody в одной таблице: валидно, удобно для двух групп строк.
<tfoot>...</tfoot><tfoot>...</tfoot> - два блока tfoot в одной таблице: tfoot допускается только один.
Подпись к таблице: caption
<caption> - название или краткое описание таблицы, которое должно идти самым первым потомком внутри <table>, ещё до <thead>. В отличие от обычного заголовка (например, <h3>), написанного просто РЯДОМ с таблицей визуально, caption программно связан именно с этой таблицей - он становится её "доступным именем", которое скринридер озвучивает перед тем, как перейти к содержимому. На таблицу допускается только один caption. Если таблица - единственное содержимое тега <figure>, вместо caption для подписи используют figcaption (см. отдельный урок про figure и figcaption).
<table><caption>Расходы за месяц</caption><thead>... - caption сразу после открывающего table, до thead.
<h3>Расходы за месяц</h3><table><thead>... - заголовок рядом с таблицей выглядит похоже, но программной связи с самой таблицей у него нет.
Частые ошибки новичков
| Ошибка | Что происходит | Как исправить |
|---|---|---|
| Думать, что порядок thead/tbody/tfoot в коде определяет их порядок на экране | tfoot всегда визуально внизу таблицы независимо от места в разметке | Для читаемости кода всё равно писать в логичном порядке: thead, затем tbody, затем tfoot |
| Не писать tbody, опасаясь что без него разметка невалидна | Браузер сам достраивает tbody в DOM при отрисовке - ошибки нет | tbody можно не писать явно, но для таблиц с несколькими группами строк писать его самому понятнее |
| Добавлять больше одного caption или tfoot на таблицу | Спецификация допускает только один caption и один tfoot | Оставлять только один экземпляр каждого из этих тегов |
| Ставить caption не первым потомком table, а например после thead | Нарушает модель содержимого таблицы | caption должен идти сразу после открывающего тега table |
| Заменять caption обычным заголовком (h3, p) рядом с таблицей | У визуального заголовка нет программной связи с таблицей для скринридера | Использовать настоящий caption внутри table |
Шпаргалка
| Тег или задача | Что нужно знать |
|---|---|
| <thead> | Шапка таблицы (заголовки столбцов) - допускается только один |
| <tbody> | Основные данные - можно несколько, браузер добавит сам, если не написать |
| <tfoot> | Итоговая строка - всегда рендерится внизу, независимо от места в коде |
| <caption> | Подпись таблицы - первый потомок table, доступное имя для скринридера |
| Печать длинной таблицы | thead может повторяться на каждой странице (Firefox/Chrome) |
| "Прилипающий" заголовок при прокрутке | CSS position: sticky на ячейках thead |
Коротко: отвечаем на вопросы
Нужно ли обязательно писать tbody, если в таблице нет явных групп строк?
Нет, не обязательно. Если tbody не написан в коде, браузер всё равно добавит его сам при отрисовке страницы - разметка остаётся рабочей. Но для сложных таблиц с несколькими смысловыми группами строк или для точной стилизации через CSS явный tbody делает код понятнее.
Почему tfoot оказался внизу таблицы, хотя я написал его в начале кода?
Так и должно быть - это не ошибка. Браузер всегда показывает группы строк в порядке шапка-тело-подвал независимо от того, в каком порядке они написаны в HTML-коде: tfoot окажется внизу, даже если формально стоит перед thead и tbody.
Чем caption отличается от обычного заголовка (например h3) над таблицей?
Визуально они могут выглядеть похоже, но caption программно связан именно с этой таблицей - он становится её доступным именем, которое скринридер озвучивает перед содержимым. У заголовка, написанного просто рядом с таблицей отдельным тегом, такой связи нет.
Можно ли сделать заголовок таблицы "прилипающим" при прокрутке?
Да, для этого ячейкам thead задают CSS position: sticky с top: 0 - тогда шапка остаётся видимой при прокрутке длинной таблицы. Стоит учитывать, что не все браузеры и не все варианты вёрстки таблиц одинаково хорошо поддерживают sticky-позиционирование внутри table.