Тег td (от английского table data, «данные таблицы») создаёт ячейку: одну клетку на пересечении строки и колонки. В ячейку записывают всё, что допустимо в обычном потоке страницы: текст, абзацы, списки, формы, картинки и даже вложенную таблицу. Заголовки колонок и строк задаёт th, а td несёт сами данные.
В уроке «Строки tr и ячейки td» показано, как ячейки складываются в строки и в сетку. Здесь разберём пределы и подводные камни: до скольких можно растянуть colspan и rowspan, какие атрибуты ячейки устарели, а какие остались рабочими, почему ячейка получается шире заданной ширины, что делать с длинным словом и многоточием, как ведут себя пустые и скрытые ячейки, как посчитать колонку и закрепить её при прокрутке.
<table>
<tr>
<td>Сенча</td>
<td>100</td>
<td colspan="2">50 г</td>
</tr>
</table>
В этой статье
- Тег td в HTML: что это и что записывают внутри
- Объединение ячеек td: пределы colspan и rowspan
- Атрибуты td: что устарело и как работает headers
- Ширина и высота td: content-box, длинное слово и многоточие
- Выравнивание, пустые и скрытые ячейки td
- Колонка таблицы и ячейки td: nth-child и colspan
- Закрепление колонки td при горизонтальной прокрутке
- Тег td и JavaScript: cellIndex, colSpan и closest
- Частые ошибки с тегом td
Тег td в HTML: что это и что записывают внутри
Ячейка td записывается только внутри строки tr. Если записать её прямо в table, валидатор Nu сообщит об ошибке, а браузер сам создаст вокруг ячейки tbody и tr. Какие ещё теги браузер достраивает вокруг ячеек, описано на странице tr. Закрывающий </td> можно опустить: ячейка кончается там, где начинается следующая или закрывается строка.
Внутри допустимы абзацы, списки, формы, заголовки, другие таблицы. Пустая ячейка тоже допустима и остаётся ячейкой: она занимает место в сетке. Ссылка, записанная в ячейке, по умолчанию занимает только свой текст, а не всю ячейку; чтобы ссылка закрыла ячейку, ячейке убирают отступы, а ссылке задают display: block и свои отступы. Если соседняя ячейка строки выше, ссылка закроет ячейку по всей ширине, а по высоте только свою часть.
| Запись | Что получается | Пример записи |
|---|---|---|
Текст в td | Ячейка с данными | <td>100</td> |
| Абзацы и списки | Допустимы: ячейка вытягивается по содержимому | <td><p>Чай</p><ul>...</ul></td> |
Вложенная table | Допустима: полная таблица внутри ячейки | <td><table>...</table></td> |
Форма или input | Допустимы: поле стоит в ячейке | <td><input type="text"></td> |
| Пустая ячейка | Допустима: остаётся местом в сетке | <td></td> |
td прямо в table | Браузер создаёт tbody и tr, валидатор считает ошибкой | <table><td>100</td></table> |
Пример: что записывают внутри ячейки
Выберите содержимое: вверху виден код, под ним — что получилось на экране.
Допустимые места и содержимое перечислены в описании элемента в стандарте HTML.
Объединение ячеек td: пределы colspan и rowspan
Атрибут colspan растягивает ячейку по колонкам, rowspan — по строкам. Основы разобраны в уроке про объединение ячеек, а здесь разберём границы значений. У colspan допустимы числа от 1 до 1000, у rowspan — от 0 до 65534. Недопустимое значение браузер исправляет молча, а валидатор Nu считает его ошибкой.
Значения 0, отрицательные числа и слова у colspan превращаются в 1, дробное 2.7 обрезается до 2, а 1001 — до 1000. У rowspan значение 0 допустимо и растягивает ячейку до конца секции (thead, tbody или tfoot), 70000 превращается в 65534. За пределы секции ячейка не выходит: в таблице с шапкой и телом объединение обрезается по границе секции.
| Запись | Что получается | Пример записи |
|---|---|---|
colspan 2 | Ячейка занимает две колонки | <td colspan="2">Скидка</td> |
colspan больше числа колонок | Появляются лишние колонки без ширины, валидатор считает ошибкой | <td colspan="5">Скидка</td> |
colspan="0" | Браузер читает как 1, валидатор считает ошибкой | <td colspan="0">Скидка</td> |
rowspan 2 | Ячейка занимает две строки | <td rowspan="2">Чай</td> |
Лишняя ячейка в строке под rowspan | Таблица получает лишнюю колонку | <tr><td>1</td><td>2</td><td>3</td></tr> |
rowspan="0" | Ячейка тянется до конца секции, валидатор не возражает | <td rowspan="0">Чай</td> |
rowspan за пределы секции | Объединение обрезается, валидатор считает ошибкой | <td rowspan="9">Чай</td> |
Пример: пределы colspan и rowspan
Выберите запись: вверху виден код, под ним — что получилось и что сказал бы валидатор.
Атрибуты td: что устарело и как работает headers
У ячейки три рабочих атрибута: colspan, rowspan и headers. Остальные устарели: Chrome их выполняет, а валидатор Nu выдаёт предупреждение. Это width, height, align, valign, bgcolor, nowrap, background, abbr, axis, char и charoff. Правило CSS перебивает устаревший атрибут, поэтому при переходе на стили вёрстка не ломается.
Атрибут headers нужен сложным таблицам, где заголовок нельзя определить по положению ячейки. В нём перечисляют через пробел значения id ячеек th, к которым относится данная ячейка: скринридер читает их вместе с данными. Ссылка на несуществующий id или на ячейку td считается ошибкой. Для обычных таблиц хватает scope у th: у td этот атрибут устарел.
| Атрибут | Что получается | Пример записи |
|---|---|---|
width, height | Устарели: размеры применяются, валидатор предупреждает | Свойства width и height |
align | Устарел: выравнивает текст, валидатор предупреждает | Свойство text-align |
valign | Устарел: выравнивает по вертикали, валидатор предупреждает | Свойство vertical-align |
bgcolor, background | Устарели: красят фон, валидатор предупреждает | Свойство background |
nowrap | Устарел: запрещает перенос, валидатор предупреждает | td { white-space: nowrap; } |
abbr, axis, char, charoff, scope | Устарели у td, валидатор предупреждает | Убрать атрибут |
headers | Рабочий: связывает ячейку с заголовками th | <td headers="chay cena">100</td> |
headers на несуществующий id | Валидатор считает ошибкой | <td headers="net">100</td> |
Пример: устаревшие атрибуты и их замена
Выберите атрибут: вверху виден код, под ним — что сделал браузер.
<table>
<tr>
<th id="chay">Чай</th>
<th id="cena">Цена</th>
</tr>
<tr>
<td headers="chay">Сенча</td>
<td headers="cena">100</td>
</tr>
</table>
td {
width: 110px;
height: 40px;
text-align: center;
vertical-align: top;
background: #ffd9a8;
white-space: nowrap;
}
Ширина и высота td: content-box, длинное слово и многоточие
Ширина и высота ячейки считаются как у обычного блока: content-box, то есть отступы и рамки добавляются сверху. Ячейка с width: 100px, отступами по 10 px и рамкой по 1 px получается шириной 122 px. Если нужно, чтобы заданная ширина включала отступы, добавляют box-sizing: border-box. Внешние отступы margin у ячейки не действуют. Поведение max-width и min-width у ячеек стандарт CSS 2.1 не определяет, а Chrome их учитывает.
Ширина одной ячейки задаёт ширину всей колонки: ячейки других строк в этой колонке получают такую же ширину. Подробнее о ширине колонок — на странице col. Высота ячейки задаёт минимум для строки. Длинное слово без пробелов растягивает колонку: overflow-wrap: break-word минимальную ширину ячейки не уменьшает, а overflow-wrap: anywhere и word-break: break-all уменьшают.
| Запись | Что получается | Пример записи |
|---|---|---|
width с отступами | Ячейка шире заданной на сумму отступов и рамок | td { width: 100px; padding: 10px; } |
box-sizing: border-box | Заданная ширина включает отступы и рамки | td { box-sizing: border-box; } |
width у одной ячейки | Задаёт ширину всей колонки | td:first-child { width: 150px; } |
margin у ячейки | Не действует | td { margin: 10px; } |
| Длинное слово | Растягивает колонку и таблицу | <td>ВсёОдним...</td> |
overflow-wrap: break-word | Слово по-прежнему растягивает колонку | td { overflow-wrap: break-word; } |
overflow-wrap: anywhere | Слово переносится по буквам | td { overflow-wrap: anywhere; } |
Многоточие без max-width | Не срабатывает: ячейка растягивается по тексту | td { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } |
Многоточие с max-width: 0 | В Chrome срабатывает: текст обрезается | td { max-width: 0; } к тем же свойствам |
Пример: ширина, длинное слово и многоточие
Выберите правило: вверху виден код, под ним таблица и размеры ячейки, которые считает сама страница.
Многоточие начинает работать и при table-layout: fixed у таблицы: ширина колонок тогда не зависит от содержимого.
td {
max-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
Выравнивание, пустые и скрытые ячейки td
По вертикали ячейка по умолчанию выравнивает содержимое по середине: vertical-align: middle. Значения top и bottom прижимают текст к верху или низу, baseline выравнивает первые строки всех ячеек строки по общей базовой линии. Свойство vertical-align действует на ячейке, а не на тексте внутри неё, и наследуется от строки и секции, как описано на странице tr. По горизонтали работает text-align.
Пустая ячейка, которая должна выглядеть пустой, получает empty-cells: hide: скрываются её рамка и фон. Свойство действует только при border-collapse: separate, а при collapse игнорируется. Пробел и комментарий внутри ячейки пустоту не нарушают, а неразрывный пробел нарушает.
Скрывать ячейку надо с оглядкой. Если у одной td задать display: none, она выпадает из сетки строки, и остальные ячейки строки сдвигаются влево, а колонка рассыпается. Без сдвига ячейку скрывает visibility: hidden: место остаётся. Всю колонку скрывают display: none у ячеек всех строк.
| Запись | Что получается | Пример записи |
|---|---|---|
vertical-align: top | Текст прижат к верху ячейки | td { vertical-align: top; } |
vertical-align: middle | Текст по середине: так по умолчанию | td { vertical-align: middle; } |
vertical-align: bottom | Текст прижат к низу ячейки | td { vertical-align: bottom; } |
empty-cells: hide | Пустая ячейка без рамки и фона, если нет collapse | td { empty-cells: hide; } |
display: none у одной ячейки | Колонка рассыпается | td:nth-child(2) { display: none; } |
display: none у всей колонки | Колонка исчезает, сетка ровная | td:nth-child(2) { display: none; } |
visibility: hidden | Ячейка невидима, место остаётся | td { visibility: hidden; } |
Пример: выравнивание, пустые и скрытые ячейки
Выберите правило и при желании включите border-collapse: collapse: результат виден сразу.
Колонка таблицы и ячейки td: nth-child и colspan
В таблице нет элемента «колонка», по которому можно было бы выбрать все ячейки: колонку собирают из ячеек строк. Селектор td:nth-child(2) берёт вторую ячейку каждой строки, и пока строки одинаковой длины, это вторая колонка. Как только в одной строке есть ячейка с colspan, счёт сбивается: вторая ячейка строки оказывается уже в третьей колонке.
Правильно посчитать колонку может скрипт: он складывает значения colSpan ячеек строки и сравнивает сумму с номером колонки. Для первой и последней ячейки строки хватает td:first-child и td:last-child, если число ячеек в строках одинаково.
| Запись | Что получается | Пример записи |
|---|---|---|
td:nth-child(2) | Вторая ячейка каждой строки | td:nth-child(2) { ... } |
td:nth-child(2) при colspan | Красит ячейку не из той колонки | <td colspan="2">Акция</td> |
Скрипт с суммой colSpan | Находит ячейки нужной колонки | col += cell.colSpan; |
td:first-child, td:last-child | Первая и последняя ячейки строки | td:last-child { text-align: right; } |
Пример: счёт колонки
Выберите способ: вверху виден код, под ним таблица. Во второй строке ячейка объединяет две колонки.
Закрепление колонки td при горизонтальной прокрутке
Первую колонку закрепляют при горизонтальной прокрутке таблицы: ячейкам колонки задают position: sticky и left: 0, а таблицу оборачивают в блок с overflow-x: auto. Без фона закреплённая колонка просвечивает: прокручиваемые ячейки видны сквозь неё. Закрепление шапки при вертикальной прокрутке описано на странице thead, а подвала — на странице tfoot.
Есть и отдельная ловушка с границей. При border-collapse: collapse граница закреплённой ячейки не остаётся на месте и пропадает. Полосу справа от колонки рисуют через box-shadow: inset -3px 0 0 цвет: тень остаётся при любом режиме границ.
| Запись | Что получается | Пример записи |
|---|---|---|
| Без правил | Колонка уезжает за левый край блока | td:first-child { } |
position: sticky; left: 0 | Колонка закреплена, но просвечивает | td:first-child { position: sticky; left: 0; } |
| Закрепление и фон | Колонка закреплена и закрывает остальные | td:first-child { background: #fff7d6; } |
border-right при collapse | Граница пропадает | td:first-child { border-right: 3px solid; } |
box-shadow: inset | Полоса справа остаётся | td:first-child { box-shadow: inset -3px 0 0 #c0292e; } |
Пример: закреплённая первая колонка
Выберите правило: вверху виден код, под ним блок с прокруткой, уже сдвинутый вправо.
.scroll {
overflow-x: auto;
}
td:first-child {
position: sticky;
left: 0;
background: #fff7d6;
box-shadow: inset -3px 0 0 #c0292e;
}
Тег td и JavaScript: cellIndex, colSpan и closest
Ячейка в скрипте представлена интерфейсом HTMLTableCellElement, общим для td и th. Ячейки строки возвращает свойство cells, о нём подробнее на странице tr. Свойство cellIndex возвращает номер ячейки в строке, а не номер колонки: при colspan эти числа расходятся. Свойства colSpan и rowSpan читают и записывают числа, headers возвращает строку с id.
Запись colSpan = 0 оставляет в атрибуте 0, а при чтении возвращает 1. Запись rowSpan = 70000 возвращает 65534. Обращение row.cells[1] во всех строках цикла ломается на строке с одной ячейкой: на месте второй ячейки undefined, а textContent у него вызывает ошибку.
| Запись | Что делает | Пример записи |
|---|---|---|
cell.cellIndex | Возвращает номер ячейки в строке, не номер колонки | cell.cellIndex |
cell.colSpan = 3 | Растягивает ячейку на три колонки | cell.colSpan = 3 |
cell.colSpan = 0 | В атрибуте 0, при чтении 1 | cell.colSpan = 0 |
row.cells[1] | Вторая ячейка строки, у короткой строки undefined | row.cells[1] |
e.target.closest("td, th") | Находит ячейку, в которой произошёл клик | e.target.closest("td, th") |
row.insertCell(-1) | Добавляет в конец строки ячейку td | row.insertCell(-1) |
Пример: свойства ячейки
Выберите действие: вверху виден код, под ним таблица и результат, который считает сама страница.
table.addEventListener('click', (event) => {
const cell = event.target.closest('td, th');
if (!cell || !table.contains(cell)) return;
console.log(cell.cellIndex, cell.colSpan, cell.textContent);
});
Частые ошибки с тегом td
Типичные ошибки с ячейками и как их исправить.
| Ошибка | Что происходит | Как исправить |
|---|---|---|
colspan="0" или слово в colspan | Браузер читает как 1, валидатор считает ошибкой | Писать число от 1 до 1000 |
colspan больше числа колонок | Лишние колонки без ширины, валидатор считает ошибкой | Считать колонки по самой длинной строке |
rowspan за пределы секции | Объединение обрезается, валидатор считает ошибкой | Объединять строки в пределах одной секции |
Лишняя ячейка в строке под rowspan | Таблица получает лишнюю колонку | Убрать ячейку, которую закрывает объединение |
Атрибуты width, height, align, valign, bgcolor | Устарели, валидатор предупреждает | Свойства width, height, text-align, vertical-align, background |
headers на несуществующий id или на td | Валидатор считает ошибкой | Ссылаться на id существующих th |
td прямо в table | Браузер достраивает tbody и tr, валидатор считает ошибкой | Записывать td в tr |
width с отступами | Ячейка шире заданной | box-sizing: border-box |
margin у ячейки | Не действует | Отступы задавать через padding или border-spacing |
Длинное слово и break-word | Колонка по-прежнему растянута | overflow-wrap: anywhere |
Многоточие без max-width | Текст не обрезается | max-width: 0 |
empty-cells: hide при collapse | Рамка пустой ячейки видна | Использовать border-collapse: separate |
display: none у одной ячейки | Колонка рассыпается | visibility: hidden |
td:nth-child() в строке с colspan | Красит ячейку не той колонки | Считать колонку в скрипте по colSpan |
Граница закреплённой колонки при collapse | Граница пропадает | box-shadow: inset -3px 0 0 #c0292e |
| Закреплённая колонка без фона | Ячейки просвечивают | Задать ячейкам фон |
cellIndex как номер колонки | При colspan числа расходятся | Складывать значения colSpan |
row.cells[1] в строке с одной ячейкой | Возвращает undefined | Проверять длину row.cells |