kirky
kirky

Reputation: 80

Pure CSS scrolling tbody and static thead table w/ dynamic height & width

I was tasked with creating a table that would be used as the major portion of a layout. The header of the table should be static while body should scroll if necessary. The issue is that if the scrollbar is needed, the columns of the table becomes misaligned because the width of the tbody changes.

I used a bit of javascript to compensate by setting the right padding on the thead to be equal to the width of the scrollbar, and planned to use a listener to remove / add back the padding as the scrollbar disappeared / appeared.

While this approach worked to keep the columns aligned, I was asked to come up with an HTML/CSS only solution if possible. Does anyone know of a way to achieve this without any js? Thanks.

Some relevant CSS I currently have:

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

table tbody {
  position: absolute;
  top: 24px;
  left: 0;
  right: 0;
  bottom: 0;
  overflow-y: auto;
}

table thead {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
}

Here is a fiddle to see the whole example: http://jsfiddle.net/kirky93/qqv73kjo/5/
Drag the viewport up/down to make the scrollbar dis/appear to see the issue.

Upvotes: 3

Views: 8985

Answers (2)

misterManSam
misterManSam

Reputation: 24702

To eliminate the misalignment with pure CSS, we can get the scrollbar to push the header aside. We can do this by making it position: fixed so that the scroll bar on the tbody extends to the top of the viewport.

Too much to read? Skip to the example at the bottom.

Column colors and widths

Use seven <col> elements, one to represent each column. We can place the class on them with width and background color and these properties will be repeated for the entire column without having to put a class on each table cell. They look like this:

<table class="fixed">      
  <col class="one">
  <col class="two">
  <col class="three">
  <col class="four">
  <col class="five">
  <col class="six">
  <col class="seven">

In order to place a width CSS property on the columns, we can set table-layout: fixed on the table element. No matching min/max widths are needed now.

Getting the thead to scroll

  • Set position: fixed
  • Set display: table to make it behave like a table again
  • Set table-layout: fixed so that the width behave properly
  • Because the thead is now effectively removed from its table and <col> elements, set the column classes on each th

The tbody

  • Leave it at its initial position: static
  • Set a top margin the same height as the thead

Full Example

Note the box-sizing: border-box which makes elements include padding and borders into their widths and heights.

html {
  box-sizing: border-box;
}
*,
*:before,
*:after {
  box-sizing: inherit;
}
body {
  margin: 0;
}
table {
  border-collapse: collapse;
  background: #FFF;
  table-layout: fixed;
  width: 100%;
}
table thead {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  background: #FFF;
  display: table;
  table-layout: fixed;
  border: solid 1px #000;
}
table tbody {
  margin-top: 24px;
}
table {
  /*Make sure table has border that matches the cell border so it is included in the width*/
  border: 1px solid black;
}
td,
th {
  height: 20px;
  border: 1px solid black;
}
.one {
  width: 30px;
  background-color: yellow;
}
.two {
  width: 30px;
  background-color: orange;
}
.three {
  width: 30px;
  background-color: red;
}
.four {
  width: 100%;
}
.five {
  width: 100px;
  background-color: green;
}
.six {
  width: 100px;
  background-color: lightblue;
}
.seven {
  width: 100px;
}
<table class="fixed">
  
  <col class="one">
  <col class="two">
  <col class="three">
  <col class="four">
  <col class="five">
  <col class="six">
  <col class="seven">

  <thead>
    <tr>
      <th class="one">1</th>
      <th class="two">2</th>
      <th class="three">3</th>
      <th class="four">4</th>
      <th class="five">5</th>
      <th class="six">6</th>
      <th class="seven">7</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
      <td>4</td>
      <td>5</td>
      <td>6</td>
      <td>7</td>
    </tr>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
      <td>4</td>
      <td>5</td>
      <td>6</td>
      <td>7</td>
    </tr>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
      <td>4</td>
      <td>5</td>
      <td>6</td>
      <td>7</td>
    </tr>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
      <td>4</td>
      <td>5</td>
      <td>6</td>
      <td>7</td>
    </tr>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
      <td>4</td>
      <td>5</td>
      <td>6</td>
      <td>7</td>
    </tr>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
      <td>4</td>
      <td>5</td>
      <td>6</td>
      <td>7</td>
    </tr>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
      <td>4</td>
      <td>5</td>
      <td>6</td>
      <td>7</td>
    </tr>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
      <td>4</td>
      <td>5</td>
      <td>6</td>
      <td>7</td>
    </tr>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
      <td>4</td>
      <td>5</td>
      <td>6</td>
      <td>7</td>
    </tr>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
      <td>4</td>
      <td>5</td>
      <td>6</td>
      <td>7</td>
    </tr>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
      <td>4</td>
      <td>5</td>
      <td>6</td>
      <td>7</td>
    </tr>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
      <td>4</td>
      <td>5</td>
      <td>6</td>
      <td>7</td>
    </tr>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
      <td>4</td>
      <td>5</td>
      <td>6</td>
      <td>7</td>
    </tr>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
      <td>4</td>
      <td>5</td>
      <td>6</td>
      <td>7</td>
    </tr>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
      <td>4</td>
      <td>5</td>
      <td>6</td>
      <td>7</td>
    </tr>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
      <td>4</td>
      <td>5</td>
      <td>6</td>
      <td>7</td>
    </tr>
  </tbody>
</table>

Upvotes: 3

Shai UI
Shai UI

Reputation: 51958

just set it to overflow:scroll on the tbody instead of auto

table tbody {
  position: absolute;
  top: 24px;
  left: 0;
  right: 0;
  bottom: 0;
  overflow-y: scroll;
}

Upvotes: 2

Related Questions