Олег Марков
HTML тег table - как создавать и оформлять таблицы на практике
Введение
Тег table в HTML используется для представления табличных данных. Это могут быть финансовые отчеты, расписания, сравнительные таблицы характеристик, статистика и любые другие данные, которые логично представить в виде строк и столбцов.
Для начинающих тег table часто кажется чем‑то простым. Но на практике вокруг него есть немало нюансов: правильная структура, семантика, доступность для скринридеров, взаимодействие с CSS, адаптивность под мобильные устройства. В этой статье я подробно разберу, как устроен тег table, как им правильно пользоваться и на что стоит обращать внимание в реальных проектах.
Смотрите, я шаг за шагом покажу вам, как создавать таблицы, как их верстать под дизайн и как не допускать типичных ошибок.
Структура HTML таблицы
Базовые элементы таблицы
Минимальная таблица состоит из тега table и набора строк tr, в которых находятся ячейки td.
Давайте разберемся на самом простом примере:
<table>
<tr>
<td>Ячейка 1</td>
<td>Ячейка 2</td>
</tr>
<tr>
<td>Ячейка 3</td>
<td>Ячейка 4</td>
</tr>
</table>Комментарии к структуре:
<table>— контейнер всей таблицы<tr>— строка таблицы<td>— обычная (данная) ячейка таблицы
Сейчас таблица без оформления, но уже имеет структуру из двух строк и двух столбцов.
Семантические элементы таблицы
Когда данные становятся сложнее, появляются заголовки столбцов, подвал таблицы, логические группы строк. Для этого применяются дополнительные теги:
caption— заголовок таблицыthead— шапка таблицы (заголовочная часть)tbody— тело таблицы (основные данные)tfoot— подвал таблицы (итоги, примечания)th— заголовочная ячейка строки или столбца
Давайте посмотрим на структурированный пример:
<table>
<!-- caption - общий заголовок таблицы -->
<caption>Продажи по кварталам</caption>
<!-- thead - заголовки столбцов -->
<thead>
<tr>
<th>Год</th> <!-- Заголовок столбца -->
<th>Q1</th>
<th>Q2</th>
<th>Q3</th>
<th>Q4</th>
</tr>
</thead>
<!-- tbody - основная часть таблицы -->
<tbody>
<tr>
<th>2023</th> <!-- Заголовок строки -->
<td>120</td>
<td>150</td>
<td>130</td>
<td>170</td>
</tr>
<tr>
<th>2024</th>
<td>140</td>
<td>160</td>
<td>155</td>
<td>180</td>
</tr>
</tbody>
<!-- tfoot - подвал таблицы с итогами -->
<tfoot>
<tr>
<th>Среднее</th>
<td>130</td>
<td>155</td>
<td>142.5</td>
<td>175</td>
</tr>
</tfoot>
</table>Как видите, такая структура делает таблицу более понятной и для браузера, и для пользователя, и для вспомогательных технологий (например, экранных читалок).
Когда использовать td, а когда th
- Ячейки, содержащие заголовки строк или столбцов, размечайте через
th - Все остальные ячейки данных — через
td
Покажу вам, как это влияет на восприятие таблицы:
<table>
<thead>
<tr>
<th>Имя</th> <!-- Заголовки столбцов -->
<th>Возраст</th>
<th>Город</th>
</tr>
</thead>
<tbody>
<tr>
<th>Анна</th> <!-- Заголовок строки (имя) -->
<td>29</td>
<td>Москва</td>
</tr>
<tr>
<th>Игорь</th>
<td>34</td>
<td>Санкт-Петербург</td>
</tr>
</tbody>
</table>Использование th помогает:
- браузеру — правильно выделять заголовки (обычно делает их жирными и выравнивает по центру)
- скринридеру — корректно озвучивать, какую именно ячейку слушает пользователь
- вам — логически отделять структуру от данных
Атрибуты table и базовое оформление
Ширина и высота таблицы
Сегодня ширину и высоту таблиц почти всегда задают через CSS, а не через устаревшие HTML‑атрибуты. Но разберем правильный способ.
<table class="sales-table">
<tr>
<td>Ячейка 1</td>
<td>Ячейка 2</td>
</tr>
</table>/* Здесь мы задаем ширину и авто-подбор высоты */
.sales-table {
width: 100%; /* Таблица растягивается на всю ширину контейнера */
max-width: 800px; /* Но не больше 800 пикселей */
}Высоту таблицы обычно не фиксируют, ее лучше доверить содержимому. Жесткая высота часто приводит к обрезанию контента или неаккуратным отступам.
Границы таблицы
Отображение границ — первое, что хочется настроить при работе с таблицами. Давайте разберемся.
<table class="bordered">
<tr>
<td>Ячейка 1</td>
<td>Ячейка 2</td>
</tr>
<tr>
<td>Ячейка 3</td>
<td>Ячейка 4</td>
</tr>
</table>/* Внешняя рамка таблицы */
.bordered {
border: 1px solid #333; /* Темная рамка вокруг всей таблицы */
border-collapse: collapse; /* Объединяем границы ячеек в одну линию */
}
/* Границы каждой ячейки */
.bordered td {
border: 1px solid #666; /* Более светлая рамка вокруг ячеек */
padding: 8px; /* Отступ внутри ячейки */
}Ключевое свойство здесь — border-collapse:
separate(значение по умолчанию) — границы ячеек разделены, между ними могут быть зазорыcollapse— границы схлопываются в одну линию, таблица выглядит более цельно
Внутренние отступы и выравнивание текста
Теперь вы увидите, как можно улучшить читабельность таблицы с помощью простых стилей:
.table-basic {
border-collapse: collapse;
width: 100%;
}
.table-basic th,
.table-basic td {
padding: 10px 12px; /* Отступы внутри ячеек */
text-align: left; /* Выравниваем текст по левому краю */
}
.table-basic thead {
background-color: #f2f2f2; /* Легкая заливка для шапки */
}
.table-basic tbody tr:nth-child(even) {
background-color: #fafafa; /* Полосатые строки для удобства чтения */
}Использование псевдокласса nth-child(even) помогает сделать полосатую таблицу без ручного применения классов к строкам.
Объединение ячеек: colspan и rowspan
Частая задача — объединить несколько ячеек в одну по горизонтали или вертикали. Для этого используются два атрибута:
colspan— объединяет ячейки по горизонтали (несколько столбцов)rowspan— объединяет ячейки по вертикали (несколько строк)
colspan — объединение столбцов
Давайте посмотрим, что происходит в следующем примере:
<table class="merge">
<tr>
<th>Имя</th>
<th colspan="2">Телефоны</th> <!-- Одна ячейка на два столбца -->
</tr>
<tr>
<td>Анна</td>
<td>+7 900 000-00-00</td>
<td>+7 900 000-11-11</td>
</tr>
</table>Комментарии:
colspan="2"говорит браузеру, что эта ячейка растягивается на два столбца- В следующей строке нужное количество ячеек должно соответствовать общей структуре таблицы
Здесь: 1 (Имя) + 2 (Телефоны) = 3 столбца, поэтому во второй строке тоже 3 ячейки
rowspan — объединение строк
Теперь покажу вам пример с объединением ячеек по вертикали:
<table class="merge">
<tr>
<th rowspan="2">Товар</th> <!-- Одна ячейка на две строки -->
<th>Регион</th>
<th>Продажи</th>
</tr>
<tr>
<!-- Здесь ячейка "Товар" уже занимает место, поэтому td не добавляем -->
<th>Москва</th>
<th>Санкт-Петербург</th>
</tr>
<tr>
<td>Ноутбук</td>
<td>120</td>
<td>90</td>
</tr>
</table>Главное правило: при использовании colspan и rowspan всегда проверяйте, что итоговое количество столбцов (колонок) в каждой логической строке совпадает. Неправильное сочетание может привести к "ломанию" разметки и странному поведению таблицы.
Совмещение colspan и rowspan
Бывают более сложные структуры, где объединие идет и по горизонтали, и по вертикали. Давайте разберем небольшой пример структуры расписания:
<table class="schedule">
<tr>
<th rowspan="2">Время</th> <!-- Тянется на две строки -->
<th colspan="2">Понедельник</th> <!-- Два подслота -->
</tr>
<tr>
<th>Утро</th>
<th>Вечер</th>
</tr>
<tr>
<td>10 00</td>
<td>Совещание</td>
<td>Разбор задач</td>
</tr>
</table>Здесь я показал вам принцип: сначала идет объединение в заголовке, а потом уже обычные строки выравниваются под получившуюся структуру.
Продвинутая семантика и доступность таблиц
Атрибут scope у th
Атрибут scope помогает явно указать, к чему относится заголовочная ячейка: к столбцу, строке или группе.
Варианты значений:
col— заголовок столбцаrow— заголовок строкиcolgroup— заголовок группы столбцовrowgroup— заголовок группы строк
Смотрите, я покажу вам, как это использовать:
<table class="a11y">
<thead>
<tr>
<th scope="col">Имя</th> <!-- Заголовок столбца -->
<th scope="col">Возраст</th>
<th scope="col">Город</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Анна</th> <!-- Заголовок строки -->
<td>29</td>
<td>Москва</td>
</tr>
<tr>
<th scope="row">Игорь</th>
<td>34</td>
<td>Санкт-Петербург</td>
</tr>
</tbody>
</table>Для зрительного пользователя разницы может почти не быть, зато скринридеры читают такие таблицы более корректно.
caption и скрытие заголовка визуально
Тег caption — важный элемент семантики. Он описывает, что именно содержит таблица. Иногда его нужно оставить только для скринридеров, скрыв визуально.
Вот пример с "визуально скрытым" заголовком:
<table class="stats">
<caption class="visually-hidden">
Статистика посещаемости за 2024 год
</caption>
<thead>
<tr>
<th>Месяц</th>
<th>Посетители</th>
<th>Просмотры</th>
</tr>
</thead>
<tbody>
<tr>
<td>Январь</td>
<td>12000</td>
<td>45000</td>
</tr>
<!-- Остальные строки... -->
</tbody>
</table>/* Класс для скрытия элемента, но сохранения его для скринридеров */
.visually-hidden {
position: absolute; /* Убираем элемент из потока */
width: 1px;
height: 1px;
padding: 0;
margin: -1px; /* Сдвигаем за пределы экрана */
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}Такой прием часто используется в интерфейсах, где таблица и так визуально понятна, но требуется корректная поддержка доступности.
Когда таблица уместна, а когда нет
Важно понимать: table в HTML предназначен только для табличных данных. Использовать таблицы для вёрстки макета страницы (layout) давно считается плохой практикой. Для компоновки интерфейса есть flexbox, grid и другие современные технологии CSS.
Используйте table, когда:
- у данных есть явные строки и столбцы
- логика чтения данных "по столбцам" и "по строкам" имеет смысл
- нужно показать числовую или табличную информацию: прайсы, отчеты, расписания
Не используйте table для:
- построения сетки страницы
- выравнивания блоков
- имитации колонок текста
Оформление таблиц с помощью CSS
Базовый "табличный" стиль
Давайте соберем вместе базовый набор стилей, который часто используется для таблиц в интерфейсах:
<table class="table">
<thead>
<tr>
<th>Имя</th>
<th>Роль</th>
<th>Статус</th>
</tr>
</thead>
<tbody>
<tr>
<td>Анна</td>
<td>Администратор</td>
<td>Активен</td>
</tr>
<tr>
<td>Игорь</td>
<td>Модератор</td>
<td>Заблокирован</td>
</tr>
</tbody>
</table>.table {
width: 100%; /* Таблица занимает всю ширину контейнера */
border-collapse: collapse; /* Границы схлопываются */
font-size: 14px;
}
.table th,
.table td {
padding: 8px 12px; /* Отступы внутри ячеек */
border: 1px solid #ddd; /* Светлая граница */
}
.table thead th {
background-color: #f5f5f5; /* Заливка шапки */
font-weight: 600; /* Усиленный шрифт */
}
.table tbody tr:nth-child(even) {
background-color: #fafafa; /* Полосатость для удобства чтения */
}
/* Подсветка строки при наведении мыши */
.table tbody tr:hover {
background-color: #f0f8ff;
}Как видите, этот код создает аккуратную таблицу, которая хорошо смотрится в большинстве интерфейсов.
Выравнивание чисел по правому краю
Для числовых данных чаще всего используют выравнивание по правому краю. Это помогает лучше сравнивать значения.
<table class="table finance">
<thead>
<tr>
<th>Статья</th>
<th class="num">План</th>
<th class="num">Факт</th>
</tr>
</thead>
<tbody>
<tr>
<td>Выручка</td>
<td class="num">100 000</td>
<td class="num">95 500</td>
</tr>
</tbody>
</table>.finance .num {
text-align: right; /* Выравниваем числа по правому краю */
white-space: nowrap;/* Не переносим числа на новую строку */
}Ограничение ширины столбцов и перенос текста
Иногда в таблице появляются длинные тексты. Если их не ограничивать, таблица может "разъехаться".
<table class="table long-text">
<thead>
<tr>
<th>Название</th>
<th>Описание</th>
</tr>
</thead>
<tbody>
<tr>
<td>Тариф Стандарт</td>
<td>Описание тарифа может быть очень длинным и содержать много деталей...</td>
</tr>
</tbody>
</table>.long-text {
table-layout: fixed; /* Фиксированная раскладка столбцов */
width: 100%;
}
.long-text th:nth-child(1),
.long-text td:nth-child(1) {
width: 30%; /* Первый столбец — 30% */
}
.long-text th:nth-child(2),
.long-text td:nth-child(2) {
width: 70%; /* Второй столбец — 70% */
}
.long-text td {
word-wrap: break-word; /* Разрешаем переносить слова */
}Свойство table-layout: fixed говорит браузеру рассчитывать ширину столбцов по их ширине, а не по содержимому. Это делает поведение таблицы более предсказуемым.
Адаптивные таблицы
Таблицы плохо уживаются с маленькими экранами. Широкая таблица может просто не поместиться по горизонтали. Давайте разберем несколько практичных подходов.
Простой горизонтальный скролл
Самый простой и понятный способ — позволить таблице прокручиваться по горизонтали.
<div class="table-wrapper">
<table class="table responsive">
<thead>
<tr>
<th>Месяц</th>
<th>Показатель 1</th>
<th>Показатель 2</th>
<th>Показатель 3</th>
<th>Показатель 4</th>
</tr>
</thead>
<tbody>
<tr>
<td>Январь</td>
<td>10</td>
<td>20</td>
<td>30</td>
<td>40</td>
</tr>
<!-- Другие строки... -->
</tbody>
</table>
</div>.table-wrapper {
overflow-x: auto; /* Включаем горизонтальную прокрутку при необходимости */
max-width: 100%; /* Не выходим за границы контейнера */
}
.table.responsive {
min-width: 600px; /* Минимальная ширина, при которой таблице комфортно */
}Пользователь на мобильном сможет скроллить таблицу в стороны, не ломая структуру.
Превращение таблицы в "карточки" на мобильном
Более сложный, но удобный для чтения вариант — на мобильных устройствах трансформировать таблицу в список карточек. Здесь мы используем CSS и атрибут data-label.
Вот базовый HTML:
<table class="table cards">
<thead>
<tr>
<th>Имя</th>
<th>Роль</th>
<th>Статус</th>
</tr>
</thead>
<tbody>
<tr>
<td data-label="Имя">Анна</td>
<td data-label="Роль">Администратор</td>
<td data-label="Статус">Активен</td>
</tr>
<tr>
<td data-label="Имя">Игорь</td>
<td data-label="Роль">Модератор</td>
<td data-label="Статус">Заблокирован</td>
</tr>
</tbody>
</table>/* На больших экранах все как обычная таблица */
.table.cards {
width: 100%;
border-collapse: collapse;
}
.table.cards th,
.table.cards td {
border: 1px solid #ddd;
padding: 8px 12px;
}
/* На узких экранах меняем отображение */
@media (max-width: 600px) {
.table.cards thead {
display: none; /* Скрываем шапку */
}
.table.cards,
.table.cards tbody,
.table.cards tr,
.table.cards td {
display: block; /* Делаем блоками */
width: 100%;
}
.table.cards tr {
margin-bottom: 12px; /* Отступ между "карточками" */
border: 1px solid #ddd;
border-radius: 4px;
overflow: hidden;
}
.table.cards td {
display: flex; /* Размещаем "метку" и значение в две колонки */
justify-content: space-between;
padding: 6px 10px;
border: none;
border-bottom: 1px solid #eee; /* Разделитель между строками внутри карточки */
}
.table.cards td::before {
content: attr(data-label); /* Берем текст из атрибута data-label */
font-weight: 600;
margin-right: 10px;
color: #555;
}
.table.cards td:last-child {
border-bottom: none;
}
}Теперь вы видите, как на мобильных каждое сочетание заголовок–значение превращается в аккуратную строку внутри карточки.
Вложенные таблицы
Иногда требуется разместить таблицу внутри ячейки другой таблицы. Это допустимо, но с осторожностью.
Пример: таблица заказов, где в каждой строке есть таблица товаров заказа.
<table class="orders">
<thead>
<tr>
<th>ID заказа</th>
<th>Клиент</th>
<th>Товары</th>
</tr>
</thead>
<tbody>
<tr>
<td>#101</td>
<td>Анна</td>
<td>
<!-- Вложенная таблица с товарами -->
<table class="inner">
<thead>
<tr>
<th>Товар</th>
<th>Кол-во</th>
</tr>
</thead>
<tbody>
<tr>
<td>Ноутбук</td>
<td>1</td>
</tr>
<tr>
<td>Мышь</td>
<td>2</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>.orders {
width: 100%;
border-collapse: collapse;
}
.orders th,
.orders td {
border: 1px solid #ddd;
padding: 8px;
}
/* Вложенная таблица оформляется чуть проще */
.orders .inner {
width: 100%;
border-collapse: collapse;
margin-top: 4px;
font-size: 13px;
}
.orders .inner th,
.orders .inner td {
border: 1px solid #eee;
padding: 4px 6px;
}Вложенные таблицы:
- усложняют структуру
- могут ухудшить адаптивность
- утяжеляют восприятие данных
Используйте их только тогда, когда другого удобного решения нет, и старайтесь не углубляться более чем на один уровень.
Частые ошибки при работе с table
Перепутанная структура строк и ячеек
Иногда начинающие разработчики пытаются вкладывать одни td в другие или забывают про tr.
Неправильно:
<table>
<td>Ячейка 1</td>
<td>Ячейка 2</td>
</table>Правильно всегда соблюдать иерархию:
- table
- thead / tbody / tfoot (необязательно, но желательно)
- tr
- th / td
- tr
- thead / tbody / tfoot (необязательно, но желательно)
Правильный вариант:
<table>
<tr>
<td>Ячейка 1</td>
<td>Ячейка 2</td>
</tr>
</table>Смешивание th и td без логики
Бывает, что th используется "для красоты", чтобы текст стал жирным. Это нарушает семантику.
Неправильно:
<tr>
<th>Имя</th>
<td>Анна</td>
<th>Роль</th>
<td>Админ</td>
</tr>Такая строка не содержит явной схемы "заголовок строки / столбца". Для оформления лучше использовать CSS, а не подменять семантические теги.
Использование таблиц для вёрстки макета
Пример "антипаттерна":
<table width="100%">
<tr>
<td width="20%">Меню</td>
<td>Контент</td>
</tr>
</table>Для таких задач лучше использовать CSS Grid или Flexbox. Таблица должна описывать именно данные, а не структуру страницы.
Жестко заданные ширины и высоты
Фиксирование высоты ячеек через HTML‑атрибуты или слишком жесткие CSS‑значения может привести к обрезанию текста и некрасивому виду.
Старайтесь:
- высоту не фиксировать
- ширину задавать гибко (проценты,
max-width,min-width) - тестировать таблицы с разным объемом текста
Заключение
Тег table в HTML — это мощный инструмент для представления табличных данных. Вы увидели, что за простым на первый взгляд синтаксисом скрывается целый набор возможностей: заголовки, объединение ячеек, логические блоки (thead, tbody, tfoot), продвинутая семантика через scope и caption, а также разнообразные приемы стилизации и адаптивности с помощью CSS.
Ключевые моменты, на которые важно опираться:
- table — только для табличных данных, а не для компоновки всей страницы
- используйте th для заголовков и td для данных
- группируйте части таблицы с помощью thead, tbody, tfoot для лучшей структуры и удобства
- применяйте colspan и rowspan аккуратно, всегда проверяя итоговую структуру
- не забывайте про доступность — caption, scope, логичные заголовки
- стили и адаптивность лучше выносить в CSS, а HTML оставлять максимально семантичным
Если вы будете придерживаться этих принципов и аккуратно относиться к структуре таблиц, работать с табличными данными в HTML станет предсказуемо и удобно.
Частозадаваемые технические вопросы и ответы
Как зафиксировать шапку таблицы при вертикальной прокрутке
Можно использовать position: sticky для ячеек в thead.
.table-sticky thead th {
position: sticky; /* Фиксируем относительно области прокрутки */
top: 0; /* Прилипаем к верху контейнера */
background: #fff; /* Фон, чтобы шапка не была прозрачной */
z-index: 2; /* Поднимаем над содержимым */
}Важно, чтобы контейнер таблицы имел ограниченную высоту и overflow-y: auto.
Как выровнять столбцы нескольких таблиц по одной сетке
Один из подходов — задать фиксированные ширины столбцов через классы и использовать их во всех таблицах.
.col-1 { width: 20%; }
.col-2 { width: 40%; }
.col-3 { width: 40%; }<table>
<tr>
<th class="col-1">ID</th>
<th class="col-2">Название</th>
<th class="col-3">Описание</th>
</tr>
</table>Так вы сохраните одинаковые пропорции в разных таблицах.
Как сделать сортировку по столбцам без перезагрузки страницы
Обычно это делается на JavaScript:
- Добавьте обработчик клика на заголовки th
- При клике соберите все строки tbody в массив
- Отсортируйте массив по значению нужного столбца
- Перестройте DOM, добавив строки в новом порядке
Библиотеки вроде DataTables или простые плагины сортировки помогают автоматизировать эту задачу.
Как объединить общие стили для всех таблиц сайта и при этом оставить возможность кастомизации
Создайте "базовый" класс для таблиц, а затем дополняйте его модификаторами.
.table-base { /* общие свойства */ }
.table-base--striped { /* полосатые строки */ }
.table-base--compact { /* уменьшенные отступы */ }<table class="table-base table-base--striped table-base--compact">
...
</table>Так вы избежите дублирования и сохраните гибкость.
Как сделать перенос длинных слов внутри ячейки без поломки вёрстки
Используйте комбинацию свойств:
.wrap-cell {
word-wrap: break-word; /* Перенос длинных слов */
word-break: break-word; /* Дополнительная поддержка */
}Примените этот класс к нужным td или th, где возможны очень длинные строки (например, ссылки или идентификаторы).
Постройте личный план изучения Html до уровня Middle — бесплатно!
Html — часть карты развития Frontend
100+ шагов развития
30 бесплатных лекций
300 бонусных рублей на счет
Все гайды по Html
Лучшие курсы по теме

HTML и CSS
Антон Ларичев
TypeScript с нуля
Антон Ларичев