Тег tbody в HTML: группы строк, стили и скрытие

Тег tbody (от английского table body, «тело таблицы») собирает строки с данными: всё, что не относится к шапке thead и подвалу tfoot. Секций tbody в одной таблице может быть несколько, и каждая образует отдельную группу строк. Записывать тег необязательно, но браузер почти всегда создаёт его сам, и это влияет на стили и скрипты.

Зачем таблицу делят на секции и в каком порядке они выводятся, объясняет урок «Как разделить таблицу: thead, tbody и tfoot». Здесь разберём то, что относится к самому tbody: какие секции достраивает браузер, как красить группы строк и их границы, как закреплять заголовок группы, как прокручивать и скрывать тело таблицы, как его печатать и как добавлять строки из скрипта.

HTML
<table>
  <thead>
    <tr><th>Чай</th><th>Цена</th></tr>
  </thead>
  <tbody>
    <tr><td>Сенча</td><td>100</td></tr>
  </tbody>
  <tbody>
    <tr><td>Пуэр</td><td>140</td></tr>
  </tbody>
</table>

Тег tbody в HTML: что это и что записывать внутри

Внутри tbody записывают только строки tr, а в них ячейки td и, если нужен заголовок строки, th. Своих атрибутов у тега нет, только глобальные. По умолчанию браузер выводит его отдельной секцией (display: table-row-group), а для скринридера это группа строк с ролью rowgroup.

ЗаписьЧто получаетсяПример записи
tr в tbodyСтрока данных<tr><td>Сенча</td><td>100</td></tr>
th в начале строкиЗаголовок строки, а не ячейка данных<th scope="row">Сенча</th>
Несколько tbody подрядКаждая секция выводится отдельной группой строк, колонки у всех общие<tbody>...</tbody><tbody>...</tbody>
rowspan через границу секцииChrome обрезает ячейку по границе своей секции<td rowspan="4">Зелёные</td>
Атрибуты align, valign, bgcolorУстарелиСвойства text-align, vertical-align и background

Объединение ячеек не выходит за секцию: если ячейка с rowspan="4" стоит в группе из двух строк, Chrome растянет её только на эти две, строки следующего tbody она не захватит.

HTML
<tbody>
  <tr><td rowspan="4">Зелёные</td><td>Сенча</td></tr>
  <tr><td>Улун</td></tr>
</tbody>
<tbody>
  <tr><td>Чёрные</td><td>Ассам</td></tr>
</tbody>

Пропуск тегов tbody и парсер: что достраивает браузер

По стандарту tbody стоит в table после caption, colgroup и thead, но только если у таблицы нет строк tr, записанных прямо в ней. Начальный тег можно опустить, когда первым внутри идёт tr и перед ним нет секции с опущенным закрывающим тегом. Закрывающий тег можно опустить, когда следом идёт другая секция tbody или tfoot либо таблица закончилась. Пустой tbody без начального тега не записать. Браузер достраивает пропущенное, поэтому в DOM секция tbody есть почти всегда, даже если в коде её нет.

Как записаноЧто делает браузерПример записи
Строки tr прямо в tableСоздаёт tbody и переносит строки в него: селектор table > tr ничего не находит<table><tr><td>Сенча</td></tr></table>
Строка tr после закрытого tbodyСоздаёт для неё новый tbody<tbody>...</tbody><tr>...</tr>
Строка tr после tfootСоздаёт новый tbody: в DOM он стоит после tfoot, а на экране строка выводится выше подвала<tfoot>...</tfoot><tr>...</tr>
Опущен </thead>, а <tbody> не записанСтроки с данными остаются внутри thead<thead><tr><th>Чай<tr><td>Сенча
Опущены </tbody> между секциямиДостраивает закрывающие теги, секции разделены верно<tbody><tr><td>1<tbody><tr><td>2
Текст или div внутри tbodyВыносит их из таблицы, они оказываются перед ней<tbody>Текст<tr>...</tr></tbody>

Пример: как браузер читает запись

Результат

Выберите запись: вверху виден код, внизу — что получилось на экране.

Из таблицы следует простое правило: если закрывающий тег шапки опущен, секцию tbody стоит записывать явно. Как браузер разбирает запись самого thead, описано на странице thead.

Допустимые места записи перечислены в описании элемента в стандарте HTML.

Стили tbody: чередование групп и строк, границы и высота

Наследуемые свойства color, font-size и text-align, фон секции и граница при border-collapse: collapse работают у tbody так же, как у thead: подробности есть на странице thead. У тела таблицы свои особенности: несколько секций, нумерация строк и высота.

ЗаписьЧто получаетсяПример записи
tbody:nth-child(even)Выбирает первую группу: thead считается первым дочерним элементом таблицы, если перед ним нет caption и colgroup: они тоже участвуют в счётеtbody:nth-child(even) { background: #ffd9a8; }
tbody:nth-of-type(even)Выбирает вторую группу: в счёте только теги tbodytbody:nth-of-type(even) { background: #ffd9a8; }
tbody tr:nth-child(even)Выбирает чётные строки, а нумерация начинается заново в каждой группеtbody tr:nth-child(even) { background: #ffd9a8; }
border-top у tbody + tbodyГраница между группами рисуется только при border-collapse: collapsetbody + tbody { border-top: 3px solid #c0292e; }
border-top у tbody + tbody tdГраница рисуется в любом режиме: она записана на ячейкахtbody + tbody td { border-top: 3px solid #c0292e; }
height у tbodyЗадаёт минимум: секция растягивается, и строки получают лишнее место поровнуtbody { height: 90px; }

Пример: стили групп и строк

Результат

Выберите правило и при желании включите border-collapse: collapse: закрашенное и обведённое видно сразу.

CSS
tbody tr:nth-child(even) {
  background: #f1ede7;
}
tbody:nth-of-type(even) {
  background: #eaf3f0;
}
tbody + tbody td {
  border-top: 3px solid #2c5e77;
}

Группы в tbody: заголовок группы, ссылка и закрепление

Если групп несколько, у каждой обычно есть название. Его записывают первой строкой секции: ячейка th с colspan на всю ширину таблицы и scope="rowgroup", так она относится ко всем строкам своей группы. Секции можно дать id и сослаться на группу по нему.

HTML
<tbody id="zelenye">
  <tr><th scope="rowgroup" colspan="2">Зелёные</th></tr>
  <tr><td>Сенча</td><td>100</td></tr>
  <tr><td>Улун</td><td>120</td></tr>
</tbody>
ЗаписьЧто получаетсяПример записи
th с scope="rowgroup"Заголовок группы на всю ширину таблицы, относится ко всем строкам секции<th scope="rowgroup" colspan="2">Зелёные</th>
Ссылка на id секцииПереход к группе: браузер прокручивает страницу или блок с таблицей до неё<a href="#zelenye">Зелёные</a>
tbody:targetВыбирает группу, к которой перешли по ссылке, и позволяет её подсветитьtbody:target td { background: #fff2c9; }
tbody th с position: stickyЗаголовки закрепляются у верхнего края; предыдущий не уходит вместе со своей секцией, его перекрывает следующийtbody th { position: sticky; top: 0; }
Закреплённый заголовок без фонаСтроки группы видны сквозь заголовокtbody th { background: #fde4e4; }

Пример: заголовки групп, ссылка и закрепление

Результат

Выберите вариант. В закреплённых вариантах блок прокручен вниз, и виден заголовок второй группы.

Закрепление работает так же, как у шапки: position: sticky и top. Отличие в том, что границей для закреплённой ячейки служит вся таблица, а не её секция: заголовки всех групп остаются у верхнего края, а виден последний из них, потому что он перекрывает предыдущие. Поэтому у заголовка нужен непрозрачный фон. Если вместе с ними закреплена шапка, top у заголовков групп делают равным высоте шапки, иначе они встанут на её место.

CSS
tbody th {
  position: sticky;
  top: 0;
  background: #fde4e4;
}
tbody:target td {
  background: #fff2c9;
}

Прокрутка и скрытие tbody: что работает и что нет

Часто нужно прокручивать только строки таблицы, оставив шапку на месте. Свойство overflow на tbody не действует: секция таблицы растёт по содержимому. Приём с display: block включает прокрутку, но разрывает связь колонок шапки и тела, и без table-layout: fixed они расходятся. Надёжнее обёртка с max-height и overflow: auto и закреплённая шапка: как её закрепить, описано на странице thead.

ЗаписьРезультатПример записи
overflow: auto у tbodyПрокрутки нет, секция растёт по содержимомуtbody { height: 90px; overflow: auto; }
display: block у tbodyПрокрутка есть, колонки шапки и тела разъезжаютсяtbody { display: block; height: 90px; overflow: auto; }
display: block у tbody и table-layout: fixed у шапки и строкКолонки в целом совпадают, но если у полосы прокрутки есть ширина, тело становится на неё уже, а шапка нет, и колонки слегка сдвигаютсяthead, tbody tr { display: table; width: 100%; table-layout: fixed; }
Обёртка с max-height и overflow: auto, у шапки position: stickyШапка закреплена, колонки общие.wrap { max-height: 110px; overflow: auto; }

Пример: прокрутка тела таблицы

Результат

Выберите вариант и сравните положение колонок шапки и тела. В вариантах с прокруткой тело уже прокручено вниз.

Группу строк скрывают, когда нужны фильтр или сворачиваемый раздел. Атрибут hidden и display: none убирают секцию из вывода целиком. Значение visibility: collapse тоже освобождает место, но скрытая строка продолжает влиять на ширину колонки. Значение visibility: hidden строки скрывает, а место оставляет пустым.

ЗаписьРезультатПример записи
Атрибут hiddenГруппа убрана из вывода, место и ширина колонки освобождены<tbody hidden>...</tbody>
display: noneТо же, что атрибут hiddentbody.old { display: none; }
visibility: collapseМесто освобождено, но колонка остаётся шириной по скрытым строкамtbody.old { visibility: collapse; }
visibility: hiddenГруппа невидима, а её место занятоtbody.old { visibility: hidden; }

Пример: способы скрыть группу строк

Результат

Выберите способ: в статусе указаны высота таблицы и ширина первой колонки. В первой группе есть длинное название.

Печать tbody: как не разорвать группу строк

При печати на нескольких страницах шапка таблицы повторяется, а строки tbody просто продолжаются, и граница страницы может разрезать группу посередине. В пробной таблице из семи групп по десять строк на листах A4 одна группа оказалась разорвана: часть строк осталась на одной странице, остальные перешли на следующую. Правило break-inside: avoid у tbody переносит такую группу целиком. Оно рассчитано на группы короче страницы, а сколько групп разорвётся, зависит от высоты строк. Результат видно в предпросмотре печати: Ctrl + P.

CSS
@media print {
  tbody {
    break-inside: avoid;
  }
}
ЗаписьРезультат при печатиПример записи
Обычный tbodyГруппа может разорваться между страницами<tbody>...</tbody>
tbody с break-inside: avoidГруппа, которая не помещается в остаток страницы, переносится на следующую целикомtbody { break-inside: avoid; }

Как шапка повторяется на каждой странице и как это отключить, описано на странице thead.

Тег tbody и JavaScript: tBodies, insertRow и createTBody

Секция tbody в скрипте представлена интерфейсом HTMLTableSectionElement. У таблицы есть живая коллекция tBodies со всеми секциями, а у секции свои rows, insertRow() и deleteRow(). Строки лучше добавлять в секцию, а не в таблицу: при наличии подвала вызов у таблицы отправит строку в подвал.

ЗаписьЧто делаетПример записи
table.tBodiesВозвращает живую коллекцию всех секций tbodytable.tBodies.length
table.createTBody()Создаёт секцию после последнего tbody, а если секций tbody нет, то в конце таблицыtable.createTBody();
tbody.insertRow(-1)Добавляет строку в конец секцииtbody.insertRow(-1);
table.insertRow(-1)Добавляет строку после последней строки таблицы: она попадает в ту секцию, где эта строка стоит (при наличии tfoot это он), а в таблице без строк и без tbody создаётся tbodytable.insertRow(-1);
row.rowIndexВозвращает номер строки во всей таблице, строки шапки входят в счётrow.rowIndex
row.sectionRowIndexВозвращает номер строки внутри своей секцииrow.sectionRowIndex
tbody.replaceChildren()Очищает секцию, сам тег tbody остаётсяtbody.replaceChildren();

Пример: строки и секции из скрипта

Результат

Выберите действие: вверху виден код, под ним таблица и результат, который считает сама страница.

JavaScript
const items = [{ name: 'Сенча', price: 100 }];
const tbody = document.querySelector('tbody');
tbody.replaceChildren();
for (const item of items) {
  const row = tbody.insertRow();
  row.insertCell().textContent = item.name;
  row.insertCell().textContent = item.price;
}

Сортировку делают перестановкой готовых строк: append() переносит существующую строку, а не копирует её, поэтому шапка и подвал остаются нетронутыми. Ячейки, созданные методом insertCell(), получаются тегами td; для th элемент создают через document.createElement('th').

Частые ошибки с тегом tbody

Типичные ошибки с секцией tbody и как их исправить.

ОшибкаЧто происходитКак исправить
table > tr в CSS или JavaScriptНичего не находит: строки находятся в tbody, который создал браузерtable > tbody > tr или table tr
Опущен </thead>, а tbody не записанСтроки с данными остаются в theadЗаписать <tbody> или </thead>
tbody:nth-child(even)Выбирает первую группу: thead участвует в счётеtbody:nth-of-type(even)
table.insertRow(-1) при наличии tfootСтрока попадает в подвалtbody.insertRow(-1)
tbody { overflow: auto; } для прокруткиПрокрутки нетОбёртка с max-height и overflow: auto
tbody { display: block; } для прокруткиКолонки шапки и тела разъезжаютсяОбёртка и закреплённая шапка
visibility: collapse для скрытия группыКолонка остаётся шириной по скрытым строкамАтрибут hidden или display: none
tbody { border: 1px solid; } без collapseРамка не рисуетсяДобавить border-collapse: collapse или записать границу на ячейках
Закреплённый заголовок группы без фонаСтроки группы видны сквозь заголовокtbody th { background: #fff; }
rowspan длиннее группыChrome обрезает ячейку по границе секцииСчитать строки группы и не выходить за них
Текст внутри tbodyБраузер выносит его перед таблицейЗаписывать текст в ячейки td
Группа разорвана при печатиЧасть строк на одной странице, часть на другойtbody { break-inside: avoid; }
Устаревшие align, valign, bgcolor у tbodyАтрибуты устарелиСвойства text-align, vertical-align и background
Категория
Поделиться в социальных сетях

Отправить комментарий

0 Комментарии

Отправить комментарий (0)

#buttons=(Хорошо) #days=(20)

Мы используем файлы cookie для улучшения работы сайта. Подробнее
Хорошо
×