Таблица с фиксированным положением столбцов без задания высоты и ширины тд и т - PullRequest
3 голосов
/ 31 января 2020

У меня есть таблица с фиксированными первыми тремя столбцами. Мои первые три столбца исправлены. Я хочу, чтобы остальные столбцы вычисляли свои height и width автоматически, основываясь на их содержимом.

Так что мой CSS выглядит так:

.outer {
    position:relative;
    background-color: hotpink;
}
.inner {
    overflow-x:scroll;
    overflow-y: visible;
    width: calc(100% - 1500px);
    margin-left: 18em;
    background-color: greenyellow;
}

table {
    table-layout: fixed;
    width: 100%;
}

td, th {
    vertical-align: top;
    border-top: 1px solid #ccc;
    padding: 0.8em;
    width: 150px;
    height: 42px;
    word-break: break-all;
}

.col1 {
    position:absolute;
    left: 0em;
    width: 6em;
}

.col2 {
    position:absolute;
    left: 6em;
    width: 6em;
 }

.col3 {
    position:absolute;
    left: 12em;
    width: 6em;
}

.emptyrow {
    position:absolute;
    left: 0em;
    width: 18em;
}

Это HTML таблицы:

<div class="outer">
    <div class="inner">
      <table>
        <thead>
          <tr>
            <th class="col1">Header 1</th>
            <th class="col2">Header 2</th>
            <th class="col3">Header 3</th>
            <th>Header 4</th>
            <th>Header 5</th>
            <th>Header 6</th>
            <th>Header 7</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td class="emptyrow">This column should have value.</td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
          </tr>
          <tr>
            <td class="col1">col 1 - B</td>
            <td class="col2">col 2 - B</td>
            <td class="col3">col 3 - B</td>
            <td>col 4 - B</td>
            <td>col 5 - B</td>
            <td>col 6 - B</td>
            <td>col 7 - B</td>

          </tr>
          <tr>
            <td class="col1">col 1 - C</td>
            <td class="col2">col 2 - C</td>
            <td class="col3">col 3 - C</td>
            <td>col 4 - C</td>
            <td>col 5 - C</td>
            <td>col 6 - C</td>
            <td>col 7 - C</td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>

Можно ли избежать установки ширины и высоты в td, th?

Я имею в виду избегать этого жестко закодированных значений height и width:

td, th {
  ...
  width:6em;
  height: 4em;
}

Возможно ли это сделать просто с простым CSS без JavaScript? Если реализовать это решение невозможно, было бы здорово увидеть другой подход.

Требования:

  1. Некоторые прокручиваемые столбцы могут быть пустыми
  2. Должна быть полоса прокрутки для прокрутки столбцов из «Заголовка 4»
  3. Заголовок 1-3 должен всегда оставаться на одной позиции
  4. Padding, во всех столбцах должно быть 0.8em
  5. Width и Height из td, th не должны быть жестко закодированы. Они должны соответствовать содержанию.
  6. border-top: 1px solid #ccc; это свойство должно быть сохранено
  7. Подсветка навесного ряда

Изображение того, что желательно видеть: enter image description here

Ответы [ 3 ]

5 голосов
/ 03 февраля 2020

Боюсь, что не существует идеального решения.

Я предоставлю вам разные варианты, и вам нужно решить, от чего отказаться.


Абсолютно позиционированные столбцы .

✅ Pure CSS - ✅ Старые браузеры - ❌ Гибкая ширина - ❌ Правильная подсветка

Ключ должен установить фиксированный width для первых трех столбцов, а затем установить margin-left свойство элемента .wrapped, равное общей ширине столбцов.

.wrapper {
  overflow-x: scroll;
  overflow-y: visible;
  margin-left: 18em;
  width: 15em;
  background-color: greenyellow;
}

td,
th {
  vertical-align: top;
  border-top: 1px solid #ccc;
  padding: 0.8em;
}

th, tr:nth-of-type(1) td {
  height: 1em;
}

th:nth-of-type(1),
td:nth-of-type(1) {
  position: absolute;
  left: 0em;
  width: 6em;
}

th:nth-of-type(2),
td:nth-of-type(2) {
  position: absolute;
  left: 6em;
  width: 6em;
}

th:nth-of-type(3),
td:nth-of-type(3) {
  position: absolute;
  left: 12em;
  width: 6em;
}

tr:hover td {
  background-color: yellow;
}
<div class="wrapper">
  <table>
    <thead>
      <tr>
        <th>Header-1</th>
        <th>Header-2</th>
        <th>Header-3</th>
        <th>Header-4</th>
        <th>Header-5</th>
        <th>Header-5</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td></td>
        <td></td>
        <td></td>
        <td></td>
        <td></td>
        <td></td>
      </tr>
      <tr>
        <td>col 1 - A</td>
        <td>col 2 - A</td>
        <td>col 3 - A</td>
        <td>col 4 - A (WITH LONGER CONTENT)</td>
        <td>col 5 - A</td>
        <td>col 6 - A</td>
      </tr>
      <tr>
        <td>col 1 - B</td>
        <td>col 2 - B</td>
        <td>col 3 - B</td>
        <td>col 4 - B</td>
        <td>col 5 - B</td>
        <td>col 6 - B</td>
      </tr>
      <tr>
        <td>col 1 - C</td>
        <td>col 2 - C</td>
        <td>col 3 - C</td>
        <td>col 4 - C</td>
        <td>col 5 - C</td>
        <td>col 6 - C (WITH_A_LONG_WORD)</td>
      </tr>
    </tbody>
  </table>

Абсолютно позиционированные столбцы + JavaScript.

❌ Pure CSS - ✅ Старые браузеры - ✅ Гибкая ширина - ❌ Правильная подсветка

Здесь вместо жесткого кодирования width столбцов заранее мы используем JS для вычисления и установки width.

Возможна гораздо лучшая реализация JS. Возьмите это только в качестве примера.

let wrapper = document.querySelector('.wrapper');
let cols = document.querySelectorAll('th');

let widthCol0 = cols[0].offsetWidth;
let widthCol1 = cols[1].offsetWidth;
let widthCol2 = cols[2].offsetWidth;

stylesheet = document.styleSheets[0]
stylesheet.insertRule('th:nth-of-type(1), td:nth-of-type(1) { left: 0px; position: absolute; width: ' + widthCol0 + 'px;}', 0);
stylesheet.insertRule('th:nth-of-type(2), td:nth-of-type(2) { left: ' +  widthCol0 + 'px; position: absolute; width: ' + widthCol1 + 'px;}', 0);
stylesheet.insertRule('th:nth-of-type(3), td:nth-of-type(3) { left: ' +  (widthCol0 + widthCol1) + 'px; position: absolute; width: ' + widthCol2 + 'px;}', 0);

wrapper.style.marginLeft = (widthCol0 + widthCol1 + widthCol2) + 'px';
.wrapper {
  overflow-x: scroll;
  overflow-y: visible;
  width: 15em;
  background-color: greenyellow;
}

td,
th {
  vertical-align: top;
  border-top: 1px solid #ccc;
  padding: 0.8em;
}

th, tr:nth-of-type(1) td {
  height: 1em;
}

tr:hover td {
  background-color: yellow;
}
<div class="wrapper">
  <table>
    <thead>
      <tr>
        <th>Header-1</th>
        <th>Header-2</th>
        <th>Header-3</th>
        <th>Header-4</th>
        <th>Header-5</th>
        <th>Header-5</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td></td>
        <td></td>
        <td></td>
        <td></td>
        <td></td>
        <td></td>
      </tr>
      <tr>
        <td>col 1 - A (WITH LONGER CONTENT)</td>
        <td>col 2 - A</td>
        <td>col 3 - A</td>
        <td>col 4 - A (WITH LONGER CONTENT)</td>
        <td>col 5 - A</td>
        <td>col 6 - A</td>
      </tr>
      <tr>
        <td>col 1 - B</td>
        <td>col 2 - B</td>
        <td>col 3 - B</td>
        <td>col 4 - B</td>
        <td>col 5 - B</td>
        <td>col 6 - B</td>
      </tr>
      <tr>
        <td>col 1 - C</td>
        <td>col 2 - C (WITH_A_LONG_WORD)</td>
        <td>col 3 - C</td>
        <td>col 4 - C</td>
        <td>col 5 - C</td>
        <td>col 6 - C (WITH_A_LONG_WORD)</td>
      </tr>
    </tbody>
  </table>

Закрепленные столбцы.

✅ Pure CSS - ❌ Старые браузеры - ❌ Гибкая ширина - ✅ Собственные выделите

Мне очень нравится решение position: sticky, которое показало @ Domini c. Это способ go, если вам не требуется поддержка IE.

Отбрасывание IE поддержка окупается. Вы можете иметь правильное выделение, и ширина первых трех столбцов будет адаптироваться к содержанию.

Тем не менее, даже если вам не нужно жестко кодировать width, вам нужно жестко закодировать свойство left, чтобы установить, в какой точке столбцы становятся липкими. Такого рода поражения имеют смысл иметь гибкий width. Обходного пути нет.

.wrapper {
  width: 40em;
  overflow-x: scroll;
}

td,
th {
  vertical-align: top;
  border-top: 1px solid #ccc;
  padding: 0.8em;
}

th,
tr:nth-of-type(1) td {
  height: 1em;
}

th:nth-of-type(1), td:nth-of-type(1),
th:nth-of-type(2), td:nth-of-type(2),
th:nth-of-type(3), td:nth-of-type(3) {
  background-color: white;
  position: sticky;
}

th:nth-of-type(1), td:nth-of-type(1) {
  left: 0em;
}

th:nth-of-type(2), td:nth-of-type(2) {
  left: 6em;
}

th:nth-of-type(3), td:nth-of-type(3) {
  left: 12em;
}

tr:hover td{
  background-color: yellow;
}
<div class="wrapper">
<table>
  <thead>
    <tr>
      <th>Header-1</th>
      <th>Header-2</th>
      <th>Header-3</th>
      <th>Header-4</th>
      <th>Header-5</th>
      <th>Header-6</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td>col 1 - A (WITH LONGER CONTENT)</td>
      <td>col 2 - A</td>
      <td>col 3 - A</td>
      <td>col 4 - A (WITH LONGER CONTENT)</td>
      <td>col 5 - A</td>
      <td>col 6 - A</td>
    </tr>
    <tr>
      <td>col 1 - B</td>
      <td>col 2 - B</td>
      <td>col 3 - B</td>
      <td>col 4 - B</td>
      <td>col 5 - B</td>
      <td>col 6 - B</td>
    </tr>
    <tr>
      <td>col 1 - C</td>
      <td>col 2 - C (WITH_A_LONG_WORD)</td>
      <td>col 3 - C</td>
      <td>col 4 - C</td>
      <td>col 5 - C</td>
      <td>col 6 - C (WITH_A_LONG_WORD)</td>
    </tr>
  </tbody>
</table>
</div>

Прикрепленные столбцы + JavaScript.

❌ Pure CSS - ❌ Старые браузеры - ✅ Гибкая ширина - ✅ Правильное выделение

Этот пример имеет те же значения HTML и CSS, что и выше.

Как и во втором примере, мы используем JS для вычисления width колонны. В этом случае мы используем его для переопределения свойства left. Кроме того, код JS более прост.

Почему мы устанавливаем свойство left в CSS, если мы собираемся установить его позже с JS? Потому что, если клиент не запускает JS, мы не хотим, чтобы столбцы полностью разрушались, нарушая наш макет.

let cols = document.querySelectorAll('th');

let widthCol0 = cols[0].offsetWidth;
let widthCol1 = cols[1].offsetWidth;

stylesheet = document.styleSheets[0];
stylesheet.insertRule('th:nth-of-type(2), td:nth-of-type(2) { left: ' +  widthCol0 + 'px !important;}', 0);
stylesheet.insertRule('th:nth-of-type(3), td:nth-of-type(3) { left: ' +  (widthCol0 + widthCol1) + 'px !important;', 0);
.wrapper {
  width: 40em;
  overflow-x: scroll;
}

td,
th {
  vertical-align: top;
  border-top: 1px solid #ccc;
  padding: 0.8em;
}

th,
tr:nth-of-type(1) td {
  height: 1em;
}

th:nth-of-type(1), td:nth-of-type(1),
th:nth-of-type(2), td:nth-of-type(2),
th:nth-of-type(3), td:nth-of-type(3) {
  background-color: white;
  position: sticky;
}

th:nth-of-type(1), td:nth-of-type(1) {
  left: 0em;
}

th:nth-of-type(2), td:nth-of-type(2) {
  left: 6em;
}

th:nth-of-type(3), td:nth-of-type(3) {
  left: 12em;
}

tr:hover td{
  background-color: yellow;
}
<div class="wrapper">
<table>
  <thead>
    <tr>
      <th>Header-1</th>
      <th>Header-2</th>
      <th>Header-3</th>
      <th>Header-4</th>
      <th>Header-5</th>
      <th>Header-6</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td>col 1 - A (WITH LONGER CONTENT)</td>
      <td>col 2 - A</td>
      <td>col 3 - A</td>
      <td>col 4 - A (WITH LONGER CONTENT)</td>
      <td>col 5 - A</td>
      <td>col 6 - A</td>
    </tr>
    <tr>
      <td>col 1 - B</td>
      <td>col 2 - B</td>
      <td>col 3 - B</td>
      <td>col 4 - B</td>
      <td>col 5 - B</td>
      <td>col 6 - B</td>
    </tr>
    <tr>
      <td>col 1 - C</td>
      <td>col 2 - C (WITH_A_LONG_WORD)</td>
      <td>col 3 - C</td>
      <td>col 4 - C</td>
      <td>col 5 - C</td>
      <td>col 6 - C (WITH_A_LONG_WORD)</td>
    </tr>
  </tbody>
</table>
</div>

Заключительные замечания

Вам решать, какой подход лучше всего соответствует вашим потребностям.

Лично я считаю 4-е место самым мощным, потому что:

  • Если JS отключено, столбцы рухнут за пределы оптимальной точки, но все будет работать правильно. Просто поиграйте со стандартным свойством left в CSS.

  • В IE11 таблица будет красиво возвращаться к таблице нефиксированных столбцов.

Не думаю, что это большое дело. Конечно, если вы программируете для intr anet, где IE11 является выбранным браузером, используйте первый или второй подход.

2 голосов
/ 03 февраля 2020

Вместо этого вы должны использовать липкие ячейки, это будет держать столбцы как часть потока документов. Также, если вы хотите, чтобы столбцы реагировали на ширину содержимого, вам нужно удалить table-layout: fixed.

Также вы можете удалить max-width, если хотите, это пример добавления некоторой разумной максимальной ширины к столбцам после которые они получат эллипсами (см. последний столбец).

.scroller {
  max-width: 600px;
  overflow: auto;
}

table, table * {
  box-sizing: border-box;
}

table {
  width: 100%;
  border-collapse: collapse;
  border-spacing: 0;
}

th {
  text-align: left;
}

/* borders should be on th/td so the table can collapse them */
th, td {
  border-top: 1px solid #ccc;
  padding: 0; /* Default padding should be removed */
}

/* padding, overflow ellipses etc behaves better if
   content is wrapped in an inner element */
.inner-cell {
  background: white;
  padding: 10px;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  max-width: 260px;
}

.sticky {
  position: sticky;
  left: 0;
}
.sticky-2 {
  left: 6em;
}
.sticky-3 {
  left: 12em;
}
/* Behaves better if width is set on inner el (or it moves 1px when scrolling) */
.sticky .inner-cell {
  width: 6em;
}
<div class="scroller">
  <table>
    <thead>
      <tr>
        <th class="sticky sticky-1"><div class="inner-cell">Stick 1</div></th>
        <th class="sticky sticky-2"><div class="inner-cell">Stick 2</div></th>
        <th class="sticky sticky-3"><div class="inner-cell">Stick 3</div></th>
        <th><div class="inner-cell">Normal 1</div></th>
        <th><div class="inner-cell">Normal 2</div></th>
        <th><div class="inner-cell">Normal 3</div></th>
      </tr>
    </thead>

    <tbody>
      <tr>
        <td class="sticky sticky-1"><div class="inner-cell">Sticky Val 1</div></td>
        <td class="sticky sticky-2"><div class="inner-cell">Sticky Val 2</div></td>
        <td class="sticky sticky-3"><div class="inner-cell">Sticky Val 3</div></td>
        <td><div class="inner-cell">Sticky Normal 1</div></td>
        <td><div class="inner-cell">Sticky Normal 2 looooong</div></td>
        <td><div class="inner-cell">Sticky Normal 3 something really long we don't want to display all of this</div></td>
      </tr>
    </tbody>
  </table>
</div>
1 голос
/ 31 января 2020

Прежде всего, вы используете тег повторно, избегайте этого. Второе - чтобы установить ширину и высоту автоматически, используйте

th {
width: auto;
height: auto;
}
...