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