1

I have table in the a page where I need to implement a vertical scroll only for the tbody part of the table. My table has columns of dynamic width, there's horizontal scrolling implemented if increase in width of a column causes the table to overflow. What I want is for only the body of the table to scroll on vertical overflow, but want the table header to remain visible. What I have implemented scrolls the entire table vertically

Following is my code for now. It has dummy data, as I cant post the actual code, but the structure is the same(jsfiddle link):

th,
td {
  text-align: left;
  padding: 5px;
  outline: solid 0.5px;
}

table {
  table-layout: auto;
  width: 100%;
  white-space: nowrap;
  overflow-x: scroll;
  overflow-y: scroll;
  height: 100px;
  display: block;
}

.container {
  width: 300px;
}
<div class="container">
  <table>
    <thead>
      <tr>
        <th>Title 1</th>
        <th>Name</th>
        <th>Address</th>
        <th>Col4</th>
        <th>Col5</th>
        <th>Col6</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Title 2</td>
        <td>Jane Doe</td>
        <td>dfss</td>
        <td>sdffsffsfd</td>
        <td>sfsfs</td>
        <td>sfsff</td>
      </tr>
      <tr>
        <td>Title 3</td>
        <td>John Doe</td>
        <td>sasas</td>
        <td>eeeee</td>
        <td>eEe</td>
        <td>sfff</td>
      </tr>
      <tr>
        <td>Title 4 is a long title</td>
        <td>Name1</td>
        <td>dfss</td>
        <td>sdffsffsfd</td>
        <td>sfsfs</td>
        <td>sfsff</td>
      </tr>
      <tr>
        <td>Title 5 is shorter</td>
        <td>Name 2</td>
        <td>dfsf</td>
        <td>sdfsf</td>
        <td>dfsf</td>
        <td>sdfsf</td>
      </tr>
      <tr>
        <td>Title 6</td>
        <td>Name 3</td>
        <td>sasas</td>
        <td>eeeee</td>
        <td>eEe</td>
        <td>sfff</td>
      </tr>
    </tbody>
  </table>
</div>

I have checked multitiple solutions on stackoverflow for this problem but they all set a fixed width for their columns and then use wrap the content inside if it exceeds the width. table with fixed thead and scrollable tbody is the only solution that didn't completely mess up my page, but doesn't work, it gives different column widths for columns in header and body.

All other solutions, even the ones that use nested table use fixed width column, and the ones which don't use js/jQuery which I would rather not use unless its the absolute, last ever option. Can anyone please suggest something?

Nickofthyme
  • 3,032
  • 23
  • 40
Amruta
  • 1,295
  • 1
  • 13
  • 24
  • from what I've read, what you want is: `` implements vertical scrolling by default. detect if table overflows horizontally, then also apply vertical scrolling to `` , correct? You'll now see that you'll need JS to "detect" if the table overflows so you can apply the appropriate css styles. – Abe Caymo Mar 26 '20 at 12:05
  • No no. Just simple vertical overflow. I'll edit the question if it's confusing. I only mentioned the horizontal scrolling because my columns widths are dynamic and I wanted to explain how I deal with that, AND so that I don't get suggestions to set a width for my columns or anything – Amruta Mar 26 '20 at 12:41

3 Answers3

1

To make the <tbody> scrollable :

tbody{
  display: block;
  height: 100px;
  width: 100%;
  overflow-y: scroll;
}

And if you want to the <thead> to stay fixed while the body scrolls:

thead tr{
  display: block
}
Abe Caymo
  • 193
  • 7
0

I'm unsure whether this is answering your question.

If the y axis is always to have a scroll and the x axis only to have a scroll if there is too much information

CSS

   overflow-x:auto;
   overflow-y:scroll;
Ryan Hinchliffe
  • 167
  • 1
  • 7
0

I came across this issue myself and found an alternate solution to the answer posted by @Abe Caymo

Simple non-ideal solution (by Abe)

The problem with Abe's solution is that it works fine up until you start to use thead and tfoot. Once you add these you will soon realize that the table column layout no longer syncs the column width across tbody, thead and tfoot. See demo below...

th,
td {
  text-align: left;
  padding: 5px;
  outline: solid 0.5px;
}

table {
  white-space: nowrap;
  display: block;
}

tbody{
  display: block;
  height: 100px;
  overflow-y: auto;
}
<div class="container">
  <table>
    <thead>
      <tr>
        <th>Title 1</th>
        <th>Name</th>
        <th>Address</th>
        <th>Col4</th>
        <th>Col5</th>
        <th>Col6</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Title 2</td>
        <td>Jane Doe</td>
        <td>dfss</td>
        <td>sdffsffsfd</td>
        <td>sfsfs</td>
        <td>sfsff</td>
      </tr>
      <tr>
        <td>Title 3</td>
        <td>John Doe</td>
        <td>sasas</td>
        <td>eeeee</td>
        <td>eEe</td>
        <td>sfff</td>
      </tr>
      <tr>
        <td>Title 4 is a long title</td>
        <td>Name1</td>
        <td>dfss</td>
        <td>sdffsffsfd</td>
        <td>sfsfs</td>
        <td>sfsff</td>
      </tr>
      <tr>
        <td>Title 5 is shorter</td>
        <td>Name 2</td>
        <td>dfsf</td>
        <td>sdfsf</td>
        <td>dfsf</td>
        <td>sdfsf</td>
      </tr>
      <tr>
        <td>Title 6</td>
        <td>Name 3</td>
        <td>sasas</td>
        <td>eeeee</td>
        <td>eEe</td>
        <td>sfff</td>
      </tr>
    </tbody>
    <tfoot>
      <tr>
        <th>Title 1</th>
        <th>Name</th>
        <th>Address</th>
        <th>Col4</th>
        <th>Col5</th>
        <th>Col6</th>
      </tr>
    </tfoot>
  </table>
</div>

Slightly more ideal solution

A better solution which maintains the auto table-layout is to set the thead and tfoot to position: sticky.

A few caveats and things to understand about this approach.

  • The overflow or element actually scrolling, is the div container of the table. You must have this and this is what you may use to control the size of the table. As such, the scroll bar will always be the full height of the scrollable table.
  • The background-color must be set to an opaque value otherwise the rows in the tbody will show behind the header as it passes below when scrolling.
  • The borders/outlines are much harder to get right but with a little finessing you can find a compatible style. Adding a border or outline to either thead or tfoot will not be sticky.

.container {
  height: 140px;
  min-height: 100px;
  overflow: auto;
  resize: vertical; /* only for demo */
}

thead,
tfoot {
  /* must background-color otherwise transparent will show rows underneath */
  background-color: white;
  position: sticky;
}

thead {
  margin-bottom: 0;
  top: 0;
}

tfoot {
  margin-top: 0;
  bottom: 0;
}

th,
td {
  text-align: left;
  padding: 5px;
  outline: solid black 0.5px;
}

table {
  width: 100%;
}
<div class="container">
  <table>
    <thead>
      <tr>
        <th>Title 1</th>
        <th>Name</th>
        <th>Address</th>
        <th>Col4</th>
        <th>Col5</th>
        <th>Col6</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Title 2</td>
        <td>Jane Doe</td>
        <td>dfss</td>
        <td>sdffsffsfd</td>
        <td>sfsfs</td>
        <td>sfsff</td>
      </tr>
      <tr>
        <td>Title 3</td>
        <td>John Doe</td>
        <td>sasas</td>
        <td>eeeee</td>
        <td>eEe</td>
        <td>sfff</td>
      </tr>
      <tr>
        <td>Title 4 is a long title</td>
        <td>Name1</td>
        <td>dfss</td>
        <td>sdffsffsfd</td>
        <td>sfsfs</td>
        <td>sfsff</td>
      </tr>
      <tr>
        <td>Title 5 is shorter</td>
        <td>Name 2</td>
        <td>dfsf</td>
        <td>sdfsf</td>
        <td>dfsf</td>
        <td>sdfsf</td>
      </tr>
      <tr>
        <td>Title 6</td>
        <td>Name 3</td>
        <td>sasas</td>
        <td>eeeee</td>
        <td>eEe</td>
        <td>sfff</td>
      </tr>
    </tbody>
    <tfoot>
      <tr>
        <th>Title 1</th>
        <th>Name</th>
        <th>Address</th>
        <th>Col4</th>
        <th>Col5</th>
        <th>Col6</th>
      </tr>
    </tfoot>
  </table>
</div>

The final result will look something like that below with all columns aligned respectively...

enter image description here


Also see this solution using display: grid on the table element.

Nickofthyme
  • 3,032
  • 23
  • 40