В современных веб-приложениях таблицы часто используются для отображения больших объемов данных. Однако, когда таблица становится слишком длинной или широкой, пользователи могут столкнуться с проблемами навигации. В этой статье мы рассмотрим, как создать таблицу с фиксированной шапкой, которая прилипает к верхней части экрана, и синхронизировать ширину колонок при изменении данных или размеров таблицы.
Постановка задачи
Нам нужно создать таблицу, которая:
- Имеет фиксированную шапку, которая остается видимой при прокрутке.
- Поддерживает горизонтальный скролл для тела таблицы.
- Автоматически синхронизирует ширину колонок шапки и тела таблицы.
- Адаптируется к изменению данных и размеров таблицы.
Реализация
Шаг 1: Структура компонента
Начнем с создания структуры компонента. Мы разделим таблицу на две части: шапку (table-header
) и тело (table-body
). Шапка будет фиксированной, а тело — прокручиваемым.
<template>
<div class="table-container">
<div class="table-header" ref="tableHeader">
<table style="table-layout: fixed">
<thead>
<tr>
<th v-for="(header, index) in headers" :key="index" :style="{width: columnWidths[index] + 'px'}">
{{ header }}
</th>
</tr>
</thead>
</table>
</div>
<div class="table-body" @scroll="syncScroll">
<table>
<thead style="visibility: collapse">
<tr ref="tableCols">
<th>
{{ header }}
</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, rowIndex) in rows" :key="rowIndex">
<td v-for="(cell, cellIndex) in row" :key="cellIndex">
{{ cell }}
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
Шаг 2: Логика компонента
Теперь добавим логику для синхронизации ширины колонок и обработки изменений данных.
<script>
export default {
data() {
return {
ready: false,
headers: ['Header 1', 'Header 2', 'Header 3', 'Header 4', 'Header 5'],
rows: [],
columnWidths: [200, 200, 200, 200, 200], // Начальные ширины колонок
}
},
watch: {
rows() {
if (this.ready) {
this.$nextTick(() => {
this.syncColumnWidths()
})
}
},
ready() {
this.$nextTick(() => {
this.syncColumnWidths()
})
},
},
mounted() {
this.ready = true
this.observer = new ResizeObserver(() => this.tableResized())
this.observer.observe(this.$el)
},
beforeDestroy() {
this.observer.disconnect()
this.ready = false
},
methods: {
syncScroll(event) {
this.$refs.tableHeader.scrollLeft = event.target.scrollLeft
},
syncColumnWidths() {
this.columnWidths = [...this.$refs.tableCols.children].map((col) => col.offsetWidth)
},
tableResized() {
requestAnimationFrame(() => this.syncColumnWidths())
},
},
// Следующий хук добавлен для демонстрации
created() {
// Генерация строковых данных для таблицы
function generateRows(rowsCount, colsCount, multiplier) {
return [...Array(rowsCount).keys()].map((i) => {
return [...Array(colsCount).keys()].map((j) => {
return `Row ${i + 1} Cell ${j + 1} Paragraph ${Math.pow(10, j + multiplier)}`
})
})
}
// Асинхронное добавление данных
setTimeout(() => {
this.rows = [...this.rows, ...generateRows(50, 5, 0)]
}, 200)
setTimeout(() => {
this.rows = [...this.rows, ...generateRows(50, 5, 2)]
}, 1000)
},
}
</script>
Шаг 3: Стилизация
Добавим стили для фиксированной шапки и прокручиваемого тела таблицы.
<style scoped>
.table-container {
width: 100%;
}
.table-header {
position: sticky;
top: 0;
z-index: 1;
overflow: hidden;
background-color: #fff;
}
.table-body {
overflow-x: auto;
}
table {
width: 100%;
border-collapse: collapse;
white-space: nowrap;
}
th,
td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
box-sizing: border-box;
}
</style>
Как это работает
- Фиксированная шапка:
- Шапка таблицы (
table-header
) прилипает к верхней части экрана благодаряposition: sticky
. - Горизонтальный скролл шапки синхронизируется с телом таблицы через метод
syncScroll
.
- Шапка таблицы (
- Синхронизация ширины колонок:
- В теле таблицы находится скрытая шапка (
<thead>
сvisibility: collapse
), которая используется для измерения ширины колонок. - Метод
syncColumnWidths
обновляет массивcolumnWidths
на основе ширины колонок скрытой шапки. - Ширина колонок применяется к шапке и ячейкам таблицы через
:style="{ width: columnWidths[index] + 'px' }"
.
- В теле таблицы находится скрытая шапка (
- Реактивность:
- При изменении данных (
rows
) или флагаready
вызываетсяsyncColumnWidths
, чтобы обновить ширину колонок. ResizeObserver
отслеживает изменения размеров таблицы и вызываетtableResized
, который обновляет ширину колонок.
- При изменении данных (
Заключение
Этот подход позволяет создать таблицу с фиксированной шапкой и синхронизацией ширины колонок, которая адаптируется к изменению данных и размеров таблицы. Использование ResizeObserver
и requestAnimationFrame
делает решение производительным и удобным для поддержки.
Такой компонент может быть полезен в приложениях, где требуется отображение больших объемов данных с удобной навигацией.
Добавить комментарий