Прокручиваемая таблица с фиксированными столбцами и заголовком, с современным CSS

Как сделать таблицу с таким небольшим количеством JavaScript, насколько это возможно, используя современный CSS?

Особенности, которые я пытаюсь иметь:

  • фиксированный столбец (столбцы) (расположение и ширина)
  • прокручиваемый по осям X и Y
  • реагирует по оси X (для столбцов с фиксированной шириной).

Примечание: я видел и анализировал некоторые из самых популярных / увиденных вопросов и ответов в SO, например здесь и здесь.

Я знаю, что это можно сделать с помощью обработчиков событий JavaScript для прокрутки, чтобы фиксированный столбец можно было перемещать вниз / вверх, чтобы следовать за основным столбцом. Примером функциональности, которую я пытаюсь создать (но с большим количеством сценариев), может быть такой. Мы также можем добавить MutationObserver в родительском элементе таблицы, чтобы обнаружить изменения размера и снова рассчитать размеры таблицы. Но это дорого по производительности, и, поскольку CSS также развивается и модернизируется, мне интересно, есть ли новые способы...

Есть ли решение 2017/2018 для этого, которое состоит только из CSS или как можно меньше JS?

Я хотел бы избежать необходимости реализовывать такие решения JavaScript, которые так просто ломаются:

Мои идеи:

a): использовать position: fixed; в фиксированных столбцах

Проблемы:

  • <td> Высота элементов должна быть определена или рассчитана с помощью JavaScript.
  • требуется прослушиватель события прокрутки, и нам нужно программно прокрутить фиксированный столбец

b): используйте div(s), чтобы "подделать" левый столбец и получить прокручиваемый контент в таблице

Проблемы:

  • высота div должна синхронизироваться с высотой строк таблицы
  • Семантика HTML потеряна, так как исправленный "поддельный" столбец больше не является синтаксисом таблицы

c): использование N таблиц показывает только части каждой из них, поэтому я могу иметь разметку HTML, но сохранить фиксированный заголовок / столбцы.

Проблемы:

  • повторный HTML x N
  • должны синхронизировать размеры и прокручивать с помощью JavaScript также

Используя прослушиватель события прокрутки и фиксированный левый столбец, мы можем использовать JavaScript следующим образом:

const firstColumn = [...document.querySelectorAll('table tr > *:first-of-type')];
const tableContainer = document.querySelector('.table-container');
tableContainer.addEventListener('scroll', function() {
    const currentScrollPosition = this.scrollTop * -1;
 firstColumn.forEach(el => el.style.marginTop = currentScrollPosition + 'px');
});
.table-container {
    width: 600px;
    height: 300px;
    overflow-x: scroll;
    overflow-y: scroll;
}

.table-scroller {
    width: 1000px;
}

table {
    width: 100%;
    margin-left: -13px;
    table-layout: fixed;
    border-collapse: collapse;
    border: none;
}

table th,
table td {
    padding: 0.8em;
    border: 1px solid;
    background-color: #eeeeef;
}

table th {
    background-color: #6699FF;
    font-weight: bold;
}

table tr {
    height: 60px;
    vertical-align: middle;
}

table tr > *:first-of-type {
    position: fixed;
    width: 50px;
    margin-left: 13px;
    margin-top: 0;
    height: inherit;
}
<div class="table-container">
    <div class="table-scroller">
        <table>
            <tbody>
                <tr>
                    <td>Edrward 0</td>
                    <td>32</td>
                    <td>London Park no. 0</td>
                    <td>London Park no. 0</td>
                    <td>London Park no. 0</td>
                    <td>London Park no. 0</td>
                    <td>London Park no. 0</td>
                    <td>London Park no. 0</td>
                    <td>London Park no. 0</td>
                    <td>London Park no. 0</td>
                    <td><a href="#">action</a></td>
                </tr>
                <tr>
                    <td>Edrward 1</td>
                    <td>32</td>
                    <td>London Park no. 1</td>
                    <td>London Park no. 1</td>
                    <td>London Park no. 1</td>
                    <td>London Park no. 1</td>
                    <td>London Park no. 1</td>
                    <td>London Park no. 1</td>
                    <td>London Park no. 1</td>
                    <td>London Park no. 1</td>
                    <td><a href="#">action</a></td>
                </tr>
                <tr>
                    <td>Edrward 2</td>
                    <td>32</td>
                    <td>London Park no. 2</td>
                    <td>London Park no. 2</td>
                    <td>London Park no. 2</td>
                    <td>London Park no. 2</td>
                    <td>London Park no. 2</td>
                    <td>London Park no. 2</td>
                    <td>London Park no. 2</td>
                    <td>London Park no. 2</td>
                    <td><a href="#">action</a></td>
                </tr>
                <tr>
                    <td>Edrward 3</td>
                    <td>32</td>
                    <td>London Park no. 3</td>
                    <td>London Park no. 3</td>
                    <td>London Park no. 3</td>
                    <td>London Park no. 3</td>
                    <td>London Park no. 3</td>
                    <td>London Park no. 3</td>
                    <td>London Park no. 3</td>
                    <td>London Park no. 3</td>
                    <td><a href="#">action</a></td>
                </tr>
                <tr>
                    <td>Edrward 4</td>
                    <td>32</td>
                    <td>London Park no. 4</td>
                    <td>London Park no. 4</td>
                    <td>London Park no. 4</td>
                    <td>London Park no. 4</td>
                    <td>London Park no. 4</td>
                    <td>London Park no. 4</td>
                    <td>London Park no. 4</td>
                    <td>London Park no. 4</td>
                    <td><a href="#">action</a></td>
                </tr>
                <tr>
                    <td>Edrward 5</td>
                    <td>32</td>
                    <td>London Park no. 5</td>
                    <td>London Park no. 5</td>
                    <td>London Park no. 5</td>
                    <td>London Park no. 5</td>
                    <td>London Park no. 5</td>
                    <td>London Park no. 5</td>
                    <td>London Park no. 5</td>
                    <td>London Park no. 5</td>
                    <td><a href="#">action</a></td>
                </tr>
                <tr>
                    <td>Edrward 6</td>
                    <td>32</td>
                    <td>London Park no. 6</td>
                    <td>London Park no. 6</td>
                    <td>London Park no. 6</td>
                    <td>London Park no. 6</td>
                    <td>London Park no. 6</td>
                    <td>London Park no. 6</td>
                    <td>London Park no. 6</td>
                    <td>London Park no. 6</td>
                    <td><a href="#">action</a></td>
                </tr>
                <tr>
                    <td>Edrward 7</td>
                    <td>32</td>
                    <td>London Park no. 7</td>
                    <td>London Park no. 7</td>
                    <td>London Park no. 7</td>
                    <td>London Park no. 7</td>
                    <td>London Park no. 7</td>
                    <td>London Park no. 7</td>
                    <td>London Park no. 7</td>
                    <td>London Park no. 7</td>
                    <td><a href="#">action</a></td>
                </tr>
                <tr>
                    <td>Edrward 8</td>
                    <td>32</td>
                    <td>London Park no. 8</td>
                    <td>London Park no. 8</td>
                    <td>London Park no. 8</td>
                    <td>London Park no. 8</td>
                    <td>London Park no. 8</td>
                    <td>London Park no. 8</td>
                    <td>London Park no. 8</td>
                    <td>London Park no. 8</td>
                    <td><a href="#">action</a></td>
                </tr>
                <tr>
                    <td>Edrward 9</td>
                    <td>32</td>
                    <td>London Park no. 9</td>
                    <td>London Park no. 9</td>
                    <td>London Park no. 9</td>
                    <td>London Park no. 9</td>
                    <td>London Park no. 9</td>
                    <td>London Park no. 9</td>
                    <td>London Park no. 9</td>
                    <td>London Park no. 9</td>
                    <td><a href="#">action</a></td>
                </tr>
            </tbody>
        </table>
    </div>
</div>

Но могу ли я сделать это без JavaScript?


О возможном дублировании предложения:

Этот возможный дубликат упоминается в моем вопросе, и в нем нет того, что я ищу. Этот вопрос требует, чтобы "был заморожен только один левый столбец" - его охват более широк, он касается фиксированного заголовка, фиксированных столбцов и прокручиваемого тела.
Другой вопрос и принятый ответ с 2009 года! Я думаю, что эта тема, с моей конкретной областью применения и примерами, заслуживает повторного рассмотрения. Кроме того: мое "решение JS" является лишь примером того, как JS может сломаться, я ищу современные методы CSS для этого.

2 ответа

Может быть, вы могли бы попытаться использовать последние position:sticky добиться этого. Или, по крайней мере, начать подход с меньшим количеством javascript.

div{
  overflow:auto;
  width:100%;
  height:200px;
}
td,
th {
  border: 1px solid #000;
  width: 100px;
}
th {background-color:red;}

table {
  table-layout: fixed;
  width:100%;
}
td:first-child, th:first-child {
  position:sticky;
  left:0;
  z-index:1;
  background-color:grey;
}
td:last-child, th:last-child {
  position:sticky;
  right:0;
  z-index:1;
  background-color:blue;
}
thead tr th {
  position:sticky;
  top:0;
}
th:first-child, th:last-child {z-index:2;background-color:red;}
<div>
  <table>
    <thead>
      <tr>
        <th>&nbsp;&nbsp;&nbsp;</th>
        <th>&nbsp;&nbsp;&nbsp;</th>
        <th>&nbsp;&nbsp;&nbsp;</th>
        <th>&nbsp;&nbsp;&nbsp;</th>
        <th>&nbsp;&nbsp;&nbsp;</th>
        <th>&nbsp;&nbsp;&nbsp;</th>
        <th>&nbsp;&nbsp;&nbsp;</th>
        <th>&nbsp;&nbsp;&nbsp;</th>
        <th>&nbsp;&nbsp;&nbsp;</th>
        <th>&nbsp;&nbsp;&nbsp;</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
      </tr>
      <tr>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
      </tr>
      <tr>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
      </tr>
      <tr>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
      </tr>
      <tr>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
      </tr>
      <tr>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
      </tr>
      <tr>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
      </tr>
      <tr>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
      </tr>
      <tr>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
      </tr>
    </tbody>
  </table>
</div>

Просто посмотрите на мой код с фиксированным заголовком и исправьте первый столбец.

          <style type="text/css">
    .table {
          font-family: arial, sans-serif;
  border-collapse: collapse;
}

.th {
  width: 126px;
  height: 56px;
  padding: 0px 16px;
  text-align: left;
  background-color: #fafafa;
}

.td {
  width: 118px;
  min-width: 118px;
  max-width: 118px;
  height: 56px;
  max-height: 56px;
  overflow: hidden;
  text-align: center;
  border-bottom: 2px solid #dddddd;
}
.pink_color {
  background-color: pink;
}

.table_wrapper {
  position: relative;
}
.table_scroll {
  height: 73vh;
  overflow: auto;
}
.table_wrapper table {
  width: 100%;
}

.table_wrapper table thead th .text {
  position: absolute;
  z-index: 2;
}
.tableFixHead
{ 
  overflow: auto; height: 63vh; 
}
.tableFixHead thead th 
{ 
  position: sticky; top: 0; z-index: 1; 
}


@media only screen and (min-width: 1440px) {
  .table_scroll {
    height: 77vh;
    overflow: auto;
  }
}
</style>

<div style="padding-top: 16px;">
    <div class="table_wrapper">
        <div class="tableFixHead">
            <table class="table">
                <thead>
                  <th style="position: sticky; left: 0; z-index: 3;" class="text th"></th>
                  <th class="text th">1</th>
                  <th class="text th">1</th>
                  <th class="text th">1</th>
                  <th class="text th">1</th>
                  <th class="text th">1</th>
                  <th class="text th">1</th>
                  <th class="text th">1</th>
                  <th class="text th">1</th>
                  <th class="text th">1</th>
                  <th class="text th">1</th>
                  <th class="text th">1</th>
                  <th class="text th">1</th>
                  <th class="text th">1</th>
                </thead>
                <tr>
                  <td style="position: sticky; left: 0; z-index: 2; background: #fafafa;" class="td">test</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                </tr>
                <tr>
                  <td style="position: sticky; left: 0; z-index: 2; background: #fafafa;" class="td">test</td>
                  <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                </tr>
                <tr>
                  <td style="position: sticky; left: 0; z-index: 2; background: #fafafa;" class="td">test</td>
                  <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                </tr><tr>
                  <td style="position: sticky; left: 0; z-index: 2; background: #fafafa;" class="td">test</td>
                  <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                </tr><tr>
                  <td style="position: sticky; left: 0; z-index: 2; background: #fafafa;" class="td">test</td>
                  <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                </tr><tr>
                  <td style="position: sticky; left: 0; z-index: 2; background: #fafafa;" class="td">test</td>
                  <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                </tr><tr>
                  <td style="position: sticky; left: 0; z-index: 2; background: #fafafa;" class="td">test</td>
                  <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                </tr>
            </table>
        </div>
    </div>
</div>
Другие вопросы по тегам