✍️ Adaptive table with N breakpoints

Introduction

In this short article, we will look at the layout of an adaptive table. The layout of the table today can be created in several ways, for example: using:

  • html table
  • display table
  • dispay grid

наверняка есть еще способы. В этом обзоре я буду работать с html table и использовать не очень много javascript, для того чтобы адаптировать макет к трем media breakpoint. У table есть несколько удобных инструментов:

  • table.insertRow и row.insertCell - для того чтобы добавить в таблицу строку и ячейку
  • table.rows и row.cells - для быстрого доступа по индексу

есть и другие удобные методы таблицы о которых можно прочитать на сайте MDN

Разработка

Я сделал этот пример для ответа в теме Выравнивание карточек товаров в списке. Основное условие поставленной задачи это позиции в соседних блоках, должны находиться на одной горизонтальной линии. Альтернативно предлагался вариант задавать высоту строчек с помощью javascript определяя позицию ссамой большой высотой и под нее подстраивать все остальные. Я предложил использовать табличную верстку которая специально разработана, для того чтобы все ячейки в одной троке имели одинаковую высоту.

Проблема которую предстояло решить это сделать таблицу адаптируемой к различной ширине экрана.

Для начала сделаем триггер который сработает на мену media breakpoint

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// media breakpoints
const breakpoints = [0, 500, 600, 700, 800, 900, Infinity];
const viewportChanged = () => {
  const w = document.documentElement.clientWidth;
  const columnsNew = breakpoints.findIndex((e) => w < e);
  if (columnsNew !== columns) {
    columns = columnsNew;
    fillTable();
  }
};
viewportChanged();

window.addEventListener("resize", viewportChanged);
window.addEventListener("orientationChange", viewportChanged);

Следующая задача заполнение таблицы:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// число колонок в таблице на текущем брейкпоинте
let columns;
// ссылка на таблицу с сущностями
const table = document.getElementById("table");
// количество свойств у сущности или подругому сколько строк будет занимать каждая сущность
const entityPropsCount = 3;
// число сущностей в таблице
const entityNum = 120;

/**
 * Заполняет таблицу HTML
 */
const fillTable = () => {
  table.innerHTML = ``;
  // количество строк сущностей при текущем количестве колонок
  const rowsCount = Math.ceil(entityNum / columns);
  // смещение по строчкам за счет добавления строк пробелов между сущностями
  // let offset = 0;
  for (let i = 0; i < rowsCount; i++) {
    const rows = [];
    // создаем строчки для свойств сущности
    for (let n = 0; n < entityPropsCount; n++) {
      const row = table.insertRow(-1);
      row.classList.add("row");
      rows.push(row);
    }
    // добавляем сущности
    for (let k = 0; k < columns && i * columns + k < entityNum; k++) {
      createEntity({
        rows,
        gapColumns: k !== columns - 1,
        indx: i * columns + k
      });
    }

    // добавляем строку пробел между сущностями
    if (i < rowsCount - 1) {
      const row = table.insertRow(-1);
      row.setAttribute("class", "row gap");
    }
  }
};

Каждая сущность занимает указанное количество строк в таблице, чтобы изменить это, нужно сделать правки в трех местах.

изменить переменную entityPropsCount

1
2
// количество свойств у сущности или подругому сколько строк будет занимать каждая сущность
const entityPropsCount = 3;

в функции генерации данных

1
2
3
4
5
6
7
8
9
10
11
12
13
const generateData = (entityNum) => {
  const array = [];
  for (let i = 0; i < entityNum; i++) {
    array.push({
      name:
        "Name Entity " + i + ": " + randomWords({ min: 1, max: 20, join: " " }),
      raiting: "Raiting: " + "*".repeat(randomInteger(1, 5)),
      props:
        "Props Entity " + i + ": " + randomWords({ min: 1, max: 8, join: " " })
    });
  }
  return array;
};

изменить функцию добавления в HTML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const createEntity = ({ rows, gapColumns, indx }) => {
...

    switch (i) {
      case 0:
        cell.innerText = data[indx].name;
        break;
      case 1:
        cell.innerText = data[indx].raiting;
        break;
      default:
        cell.innerText = data[indx].props;
    }

...
};

Полный код смотри в примере

Пример

Ссылка на пример если iframe не загрузился

Автор сайта Денис aka mr_dramm