3

I want to use CSS to scroll only the table body. I found a few examples but they don't work well if the table rows length is different.

I started with this example:

https://codepen.io/pdg/pen/MayBJK

HTML code:

<div class="container">
  <h1>CSS only scrollable table body</h1>
  <p>by PDG</p>
<table class="table-scroll small-first-col">
  <thead>
    <tr>
    <th>Head 1</th>
    <th>Head 2</th>
    <th>Head 3</th>
    <th>Head 4</th>
    </tr>
  </thead>
  <tbody class="body-half-screen">
    <tr>
      <td>1</td>
      <td>First row</td>
      <td>3 dsad sad sad sad sad sadsadsad sadasd asdsa</td>
      <td>4 dasd sad asdsad sad sadsa dasdsad</td>
    </tr>
    <tr>
      <td>1 dasd asd sadsadsad sadsad</td>
      <td>2dsadsadsa dsad sadasd sad sad sa</td>
      <td>A very long cell content comes here, just to test it all!!!</td>
      <td>4</td>
    </tr>
    <tr>
      <td>1</td>
      <td>2 dsad asd asd sad sad sad asdsadsad</td>
      <td>3</td>
      <td></td>
    </tr>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
      <td>There is an empty cell above!</td>
    </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
      <td>4</td>
    </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
      <td>4</td>
    </tr>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
      <td>4</td>
    </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
      <td>4</td>
    </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
      <td>4</td>
    </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
      <td>4</td>
    </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
      <td>4</td>
    </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
      <td>4</td>
    </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
      <td>4</td>
    </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
      <td> Last row</td>
    </tr>
  </tbody>
</table>
  <h3>Notes</h3>
  <p>The table uses flex properties to arrange the cell sizes, however, this tends to make them the same as "fixed width". To fix that, you need to create additional classes that target the <strong>td:nth-child()</strong> and <strong>th:nth-child()</strong> with a specific <strong>flex-basis</strong> property.</p>
  <p>The <strong>height</strong> of the body also must be fixed and cannot be percentages (in the example above I use "vh")</p>
</div>

CSS code:

.container{
  padding: 1rem;
  margin: 1rem;
}
.table-scroll{
  /*width:100%; */
  display: block;
  empty-cells: show;

  /* Decoration */
  border-spacing: 0;
  border: 1px solid;
}

.table-scroll thead{
  background-color: #f1f1f1;  
  position:relative;
  display: block;
  width:100%;
  overflow-y: scroll;
}

.table-scroll tbody{
  /* Position */
  display: block; position:relative;
  width:100%; overflow-y:scroll;
  /* Decoration */
  border-top: 1px solid rgba(0,0,0,0.2);
}

.table-scroll tr{
  width: 100%;
  display:flex;
}

.table-scroll td,.table-scroll th{
  flex-basis:100%;
  flex-grow:2;
  display: block;
  padding: 1rem;
  text-align:left;
}

/* Other options */

.table-scroll.small-first-col td:first-child,
.table-scroll.small-first-col th:first-child{
  flex-basis:20%;
  flex-grow:1;
}

.table-scroll tbody tr:nth-child(2n){
  background-color: rgba(130,130,170,0.1);
}

.body-half-screen{
  max-height: 50vh;
}

.small-col{flex-basis:10%;}

It seemed to be a good idea and in their demo everything looks fine, but on a more complex example you can see that the columns are not vertical aligned very well. See my example below:

https://jsfiddle.net/pnfu321g/

I also tried a different example: http://jsfiddle.net/hashem/CrSpu/555/

But this also doesn't work well in my case, on complex tables with rows that have different widths.

Please suggest a css solution (no javascript) that will make a table with rows of any size to look good.

UPDATE

The reason why other solutions didn't worked for me was because I was using an older version of Mozilla Firefox - 57. And that's why almost evrything that I tested didn't worked. I marked the questions as duplicate.

Pascut
  • 3,291
  • 6
  • 36
  • 64
  • This doesn't really behave like a table anymore since you've changed the display type of all its elements. In a normal table, all cells in a column have the same width, but here you've explicitly made it so that cells have different widths in a column. What is the expected result here? How do you want cells to line up? – Mathias-S Aug 20 '19 at 09:09
  • I expect the following result: If a cell width is too long, the text will break and fall on two-three lines and the cell width will be aligned with the other cells. So the table row height will increase but the cells length will be similar. – Pascut Aug 20 '19 at 09:10

2 Answers2

2

You can use position:sticky on the th elements within the table head.

Basic example (CSS):

table.sticky-headers { width:100%; }
table.sticky-headers thead tr th { text-align:left; position:sticky; top:0; background:#fff; }

Basic markup (Add more rows to get scrollbars):

<table class="sticky-headers">
    <thead>
        <tr>
            <th>Head 1</th>
            <th>Head 2</th>
            <th>Head 3</th>
            <th>Head 4</th>
            <th>Head 5</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>Content 1</td>
            <td>Content 2</td>
            <td>Content 3</td>
            <td>Content 4</td>
            <td>Content 5</td>
        </tr>
   </tbody>
</table>

Working example here: http://jsfiddle.net/yp1xb7dj/

HaukurHaf
  • 13,522
  • 5
  • 44
  • 59
  • Please add the sticky position to this example: https://jsfiddle.net/pnfu321g/ . I tried to add it to the "th" and "td" elements but the "td" elements are still not vertical aligned well. – Pascut Aug 20 '19 at 09:14
  • 3
    @Pascut You should remove all `display` properties and `flex` related properties, and set a max-width for the table or cell. The text will automaticaly wrap then. – Mathias-S Aug 20 '19 at 09:15
  • I will try your suggestion with "max-width". Thanks for the suggestion. – Pascut Aug 20 '19 at 09:16
  • There is so much going on there, what's with all the flexbox properties? Do you need the table body to have a maximum height? I'd just scrap all the CSS and start from scratch, using my example as a starting point. – HaukurHaf Aug 20 '19 at 09:16
  • I tried to remove the "display" properties and to add the "max-width" property. But if I remove the display property the "tbody" is not scrollable anymore. See this example: https://jsfiddle.net/pnfu321g/1/ – Pascut Aug 20 '19 at 09:18
  • This solution works well, I tested on an older Mozilla Firefox and there it didn't worked well. But on the latest Chrome and Mozilla versions it work like a charm! Thank you. – Pascut Aug 20 '19 at 10:01
1

If you want fixed cell width and the text in cells with more content to break and fall on two-three lines, you can try specifying the cell width and the height will be fixed automatically:

table.sticky-headers thead tr th {
    width: 20%;
    text-align:left; 
    position:sticky; 
    top:0; 
    background:#fff;
}

The result:
enter image description here

liakoyras
  • 1,101
  • 12
  • 27
  • Please remember that I want all the table body cells to be well aligned both vertical and horizontal. If I just add this header setting I won't achieve what I want. – Pascut Aug 20 '19 at 09:22
  • How exactly could the alignment be better? They are already aligned, I' m not sure about what exactly you mean. – liakoyras Aug 20 '19 at 09:24
  • Please try your solution with this HTML table example: https://jsfiddle.net/pnfu321g . Thank you. Are them looking good? Is everything aligned? – Pascut Aug 20 '19 at 09:27
  • You want for only one row at a time to be visible? – liakoyras Aug 20 '19 at 09:32
  • I want everything to be visible and well aligned. I also need the table body to be scrollable and the header to be fixed. If I scroll I want to see the header. I almost got to a solution, but now the header is not well aligned with the body, see this code: https://jsfiddle.net/pnfu321g/2/ – Pascut Aug 20 '19 at 09:35
  • 1
    This is the best I can do, otherwise I don't think I understand what you mean http://jsfiddle.net/q0x78yw4/1/ – liakoyras Aug 20 '19 at 09:40
  • It looks good, but I want the header to be fixed and to be able to scroll through the table body, like here: https://jsfiddle.net/pnfu321g/3/ – Pascut Aug 20 '19 at 09:43
  • Isn't the header already fixed and the body scrollable in my fiddle? – liakoyras Aug 20 '19 at 09:45
  • No, because if I scroll down the header is not visible anymore. Maybe you've sent a wrong URL. – Pascut Aug 20 '19 at 09:47
  • 1
    try this http://jsfiddle.net/bq7d05m4/ – liakoyras Aug 20 '19 at 09:49
  • http://prntscr.com/ov11dh the header is visible although I have scrolled. – liakoyras Aug 20 '19 at 09:50
  • 1
    I tested on Mozilla Firefox, your example works on Chrome only. It works well, but I want the solution to work on Mozilla and Safari also. – Pascut Aug 20 '19 at 09:52
  • 1
    I just tested on Mozilla and it works fine. http://prntscr.com/ov156z – liakoyras Aug 20 '19 at 09:56
  • 1
    I'm using an older version of Mozilla: 57 and yes, it works well on the newest versions. Thank you very much, I accepted your answer as the best one! Thanks for your time. – Pascut Aug 20 '19 at 09:59