Один из подходов состоит в том, чтобы иметь данные с использованием элементов white-space: nowrap
внутри ячеек таблицы и вычислять минимальную ширину каждой строки, которая будет максимальной шириной элементов данных строки. Эти "ширины разрыва" будут рассчитаны только один раз, когда таблица будет заполнена данными ( при загрузке или заново создать данные ). Имея эти "ширины разрыва" , мы можем вычислить минимальную ширину таблицы и сравнить ее с шириной основного клиента и соответственно скрыть столбцы при изменении размера окна.
Расчет "разрыва -width "
Это выполняется при загрузке или при каждом обновлении таблицы новыми данными ( т.е. изменение страницы разбивки на страницы ). Мы go через элементы TH и TD ячеек таблицы и сохраняем самую большую ширину текста ячейки из элементов span.
function calcBreakWidths() {
// Each column one base index
let thIndex = 1;
for (let th of document.querySelectorAll('thead th')) {
// Get the width of every text span of every TH column,
// this way, we'll have also header break-widths
let breakWidth = document.querySelector(`thead th:nth-child(${thIndex}) > span`).offsetWidth;
// Go through all column TD elements and keep the biggest text span width
for (let span of document.querySelectorAll(`tbody td:nth-child(${thIndex}) > span`)) {
if (span.offsetWidth > breakWidth) {
breakWidth = span.offsetWidth;
}
}
// Save the biggest text span break-width to the TH dataset
th.dataset.breakwidth = breakWidth;
// Next column index
thIndex++;
}
}
Скрыть / Показать столбцы при изменении размера окна
Вкл window.resize
мы вычисляем минимальную ширину таблицы, суммируя все ширины разрыва . Затем мы go просматриваем каждый столбец, начиная с самого правого, и проверяем, не превышает ли минимальная ширина таблицы, включая текущий столбец, ширину основного клиента; если это так, то мы скрываем текущий столбец, иначе мы показываем текущий столбец. Это делается с использованием classList.add
и classList.remove
с использованием класса с именем hidden
со стилем display: none
CSS. В конце каждой итерации столбца мы вычитаем текущий столбец ширина разрыва из минимальной ширины таблицы, чтобы получить следующую правильную минимальную ширину таблицы без текущего столбца ширина разрыва .
window.addEventListener('resize', function() {
const bodyWidth = document.body.clientWidth;
// Get all columns break-widths from the TH element dataset
const breakWidths = [...document.querySelectorAll('th')]
.map(th => parseInt(th.dataset.breakwidth));
// Sum-up all column break-widths (+2 pixels) to calculate table minimum width
let tableMinWidth = breakWidths
.reduce((total, breakWidth) => total + breakWidth + 2, 0);
for (let column = breakWidths.length; column > 0; column--) {
const tableIsLarger = tableMinWidth > bodyWidth;
// const th = document.querySelector(`th:nth-child(${column})`);
const cells = document.querySelectorAll(`th:nth-child(${column}), td:nth-child(${column})`);
// If table min width is larger than body client width,
// then hide the current column
if (tableMinWidth > bodyWidth) {
// We're hidding the column by iterating on each table cell
// and add the hidden class only if the column does not already contain
// the hidden class. We're doing this for performance reasons
if (!cells[0].classList.contains('hidden')) {
cells.forEach(cell => cell.classList.add('hidden'));
}
// Else if the table min width is not larger than body client width,
// we remove the hidden class from the column to show each column cell
} else if (cells[0].classList.contains('hidden')) {
cells.forEach(cell => cell.classList.remove('hidden'));
}
// Subtract current column break-width from the total table min width
// so to have the correct min table width for the next column
tableMinWidth -= breakWidths[column - 1] + 2;
}
});
Фрагмент показывает это в действии
Прочтите встроенные комментарии
// Each TH class is a field name
const fields = [...document.querySelectorAll('thead th')].map(el => el.className);
// Generate 20 rows with fake data
for (let i = 0; i <= 20; i++) {
const tr = document.createElement('tr');
fields.forEach(field => {
const td = document.createElement('td');
const text = document.createElement('span');
td.className = field;
text.textContent = fake(field);
td.appendChild(text);
tr.appendChild(td);
});
document.querySelector('table tbody').appendChild(tr);
}
// Calculate each column break width, the max data span element width
function calcBreakWidths() {
let thIndex = 1;
for (let th of document.querySelectorAll('thead th')) {
let breakWidth = document.querySelector(`thead th:nth-child(${thIndex}) > span`).offsetWidth;
for (let span of document.querySelectorAll(`tbody td:nth-child(${thIndex}) > span`)) {
if (span.offsetWidth > breakWidth) {
breakWidth = span.offsetWidth;
}
}
th.dataset.breakwidth = breakWidth;
thIndex++;
}
}
calcBreakWidths();
// Handle window resize and hide each column exceeds BODY client width
window.addEventListener('resize', function() {
const bodyWidth = document.body.clientWidth;
// Get the break widths saved to the TH datasets
const breakWidths = [...document.querySelectorAll('th')]
.map(th => parseInt(th.dataset.breakwidth));
// Calculate table min width (+2 pixels for border + padding for each cell)
let tableMinWidth = breakWidths
.reduce((total, breakWidth) => total + breakWidth + 2, 0);
// Loop from last to the first column and compare the widths
for (let column = breakWidths.length; column > 0; column--) {
const cells = document.querySelectorAll(`th:nth-child(${column}), td:nth-child(${column})`);
if (tableMinWidth > bodyWidth) {
if (!cells[0].classList.contains('hidden')) {
cells.forEach(cell => cell.classList.add('hidden'));
}
} else if (cells[0].classList.contains('hidden')) {
cells.forEach(cell => cell.classList.remove('hidden'));
}
tableMinWidth -= breakWidths[column - 1] + 2;
}
});
// Function to create fake data
function fake(what) {
switch (what) {
case 'name': return faker.name.findName();
case 'email': return faker.internet.exampleEmail();
case 'address': return faker.address.streetAddress();
case 'country': return faker.address.country();
case 'account': return faker.finance.accountName();
}
}
table {
border-collapse: collapse;
width: 100%;
}
table, th, td {
border: 1px solid black;
}
tbody td > span,
thead th > span {
white-space: nowrap;
background-color: gold;
}
thead th > span {
background-color: aquamarine;
}
.hidden {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Faker/3.1.0/faker.min.js" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Faker/3.1.0/locales/en/faker.en.min.js" crossorigin="anonymous"></script>
<table>
<thead>
<tr>
<th class="name"><span>Name<span></th>
<th class="email"><span>Email</span></th>
<th class="account"><span>Personal or Business Account</span></th>
<th class="address"><span>Personal or Business Address</span></th>
<th class="country"><span>Country</span></th>
</tr>
</thead>
<tbody>
</tbody>
</table>
Вы можете запустить приведенный выше фрагмент кода и попытаться изменить размер окна, но у вас будет лучшее представление об адаптивном поведении таблицы, если вы попробуйте изменить размер браузера после открытия фрагмента на Полная страница или перейдя на страницу с полным примером здесь: https://zikro.gr/dbg/so/62491859/responsive-table.html
Это не идеально, но работает плавно в моем браузере, и он скрывает столбцы справа налево в зависимости от ширины текста данных столбцов.