17

All the solutions I've seen to make a table with a sticky header and resizable columns seems to be using Jquery. I want a plain old solution using CSS only if possible. If CSS only is not possible then I can go with CSS + Javascript but no JQuery, please. Internet Explorer support is a pipe dream at this point...

I've found separate solutions for a sticky header, and separate solutions for resizable columns but no complete solution for both. Can anyone help? The only thing missing in my attempt table tbody is display:block for the scrollbar to work, but that messes everything else up. My attempt:

        table{ 
         display:block;
         background-color:cyan;
         height:300px !important; 
        }
        th, td {
         min-width: 25px;
         resize: horizontal;
         overflow: auto;
        } 
             
        tbody { 
         height: 30px !important;
         background-color:green;
         width: 100%;
         font-size: 1.45vmin;
         overflow-y: scroll; 
        }
         <div style="height:30px;background-color:red;">
            <table>
            <thead>
            <tr><td>header1</td><td>header2</td><td>header3</td><td>header4</td><td>header5</td></tr>
            </thead>
            <tbody>
         <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td></tr>
         <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
         <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
         <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
         <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
         <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
         <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
         <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
         <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
         <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
         <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
         <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
         <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
         <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
         <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
         <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
         <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
         <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
         <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
         <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
         <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
         <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
         <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
         <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
         <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
         <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
         <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
         <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
         <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
         <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
         <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
         <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
         <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
         <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
         <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
         
         </tbody>
            </table>
         </div> 
Max Alexander Hanna
  • 3,388
  • 1
  • 25
  • 35
  • 1
    You could probably have two copies of the table, one for the header with `overflow:hidden` and a fixed height. Other than that, nope. JavaScript. – Diodeus - James MacFarlane Jan 04 '19 at 19:01
  • Can you use Grid or is table a requirement? – sol Jan 08 '19 at 22:35
  • 2
    You can't make interactive resizable columns without JS because something have to handle the user input (mouse / touch screen) which is not possible by CSS alone. – venimus Jan 09 '19 at 08:12
  • 1
    For modern browsers you could use the sticky position on the `th` together with the resize property. – Erik.J Jan 13 '19 at 12:24
  • there's also [position:sticky](https://developer.mozilla.org/en-US/docs/Web/CSS/position#Sticky_positioning) (which of course doesn't work in IE) – jao Jan 15 '19 at 17:00
  • but of course i want a solution that works on ie. It may not be the main browser people use, but its still up there. Not supporting IE is not an option. – Max Alexander Hanna May 23 '19 at 15:58

4 Answers4

25

Here is a solution using the CSS position: sticky attribute with resize: horizontal.

The resizable table headers will show a small icon in the lower-right hand corner, that you can grab and resize. (Due to a bug in Chrome, it may be so small you can barely see it...so look closely.)

<html>  

    <style>
.fixed_headers {
  border-collapse: collapse;
  width:100%;
}
.fixed_headers td,
.fixed_headers thead th {
  padding: 5px;
  text-align: left;
}
.fixed_headers thead, textarea {
  background-color: #333;
  color: #FDFDFD;
}
.fixed_headers thead tr {
  position: relative;
}
.fixed_headers tbody tr:nth-child(even) {
  background-color: #DDD;
}

.fixed_headers thead th {
  position: sticky;
  top: 0;  /* REQUIRED: https://stackoverflow.com/a/43707215 */
  background-color: #333; 
  resize: horizontal;
  overflow: auto;
  min-width: 70px;
}
    </style>

        <div style="height: 300px;overflow: auto;">
        <table class="fixed_headers">
        <thead>
        <tr><th>header 1</th><th>header 2</th><th>header 3</th><th>header 4</th><th>header 5</th></tr>
        </thead>
        <tbody>
        <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td></tr>
        <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
        <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
        <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
        <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
        <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
        <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
        <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
        <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
        <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
        <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
        <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
        <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
        <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
        <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
        <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
        <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
        <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
        <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
        <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
        <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
        <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
        <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
        <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
        <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
        <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
        <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
        <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
        <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
        <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
        <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
        <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
        <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
        <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
        <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>

        </tbody>
        </table>
        </div> 
    </html>
Max Alexander Hanna
  • 3,388
  • 1
  • 25
  • 35
Ravi
  • 397
  • 5
  • 14
  • i cant resize these columns mate... what is this – Max Alexander Hanna Jan 11 '19 at 18:13
  • @MaxAlexanderHanna Now you can check updated table. Hope you get the perfect solution. Thank you :) – Ravi Jan 13 '19 at 06:33
  • My comment to @venimus regarding the text area hack also goes for this table. ;) – Erik.J Jan 13 '19 at 12:15
  • @Erik.J I have removed the textarea tag, now what you think? – Ravi Jan 15 '19 at 06:23
  • @Ravi The resize on the th is working. The scrolling is still happening on the div (try overflow:hidden on the div to check), the height and overflow on the tbody (or table) is not effective as table parts' height are grown by content. I.e. the overflow never happens. The 100% width on the tbody is referring to the table that has width auto and thus is also grown by content. – Erik.J Jan 15 '19 at 13:34
  • @Erik.J I tried to make the overflow: hidden to div but the scrolling is not working. And Yes, tbody CSS was not effective, So I removed that code. – Ravi Jan 16 '19 at 17:13
  • @Ravi The scrolling and resizing is actually working as requested, though not in the snippet run here on SO. (The suggested overflow hidden on the div was just a way to check that the scroll was on the div and not on the tbody.) – Erik.J Jan 16 '19 at 18:02
  • I thought it didn't work and couldn't figure out why it had been accepted... it took me quite a while before I figured out that there were little teeny tiny resize corners, very hard to see! I edited in a warning about that. Thanks for the answer...though it would be nice if you could resize the columns with a splitter or something similar. – HostileFork says dont trust SE Apr 15 '19 at 02:31
  • @HostileForksaysdonttrustSE I'm on Firefox and I can't see any handle at all. – Agostino Jun 01 '21 at 14:44
  • I don't see resize bars at all. I know what they look like but I'm not seeing it. – IMTheNachoMan Jun 18 '21 at 18:28
  • Anybody know if it's possible to get this working when the `table-layout` is set to `auto` and is overflowing horizontally? – m4heshd Jun 21 '21 at 23:14
10

For CSS resizable columns you could use this trick. Abuse the webkit resizable textarea feature. Wrap header titles in readonly textareas and style as headings, just leave the resizing thumb. Use min-max-width to the same to disable vertical resizing. This is just Proof-of-work. Play with it.

<html>  
 <style>
.fixed_headers {

  border-collapse: collapse;
}
.fixed_headers textarea {
  text-decoration: underline;
}
.fixed_headers textarea,
.fixed_headers td {
  padding: 5px;
  text-align: left;
}
.fixed_headers textarea,
.fixed_headers td {
    min-width: 100px;
}
.fixed_headers thead, textarea {
  background-color: #333;
  color: #FDFDFD;
}
.fixed_headers thead tr {

  position: relative;
}
.fixed_headers tbody {

  overflow: auto;
  width: 100%;
  height: 300px;
}
.fixed_headers tbody tr:nth-child(even) {
  background-color: #DDD;
}
.old_ie_wrapper {
  height: 300px;
  width: 750px;
  overflow-x: hidden;
  overflow-y: auto;
}

textarea {
  border: none;
  min-height: 16px;
  max-height: 16px;
}
</style>
    <div style="height: 300px;overflow: auto;">
    <table class="fixed_headers">
    <thead>
    <tr><th><textarea readonly>header 1</textarea></th><th><textarea readonly>header 2</textarea></th><th><textarea readonly>header 3</textarea></th><th><textarea readonly>header 4</textarea></th><th><textarea readonly>header 5</textarea></th></tr>
    </thead>
    <tbody>
    <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td></tr>
    <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
    <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
    <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
    <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
    <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
    </tbody>
    </table>
    </div> 
</html>

Demo http://jsfiddle.net/rft8v4sp/1/

venimus
  • 5,907
  • 2
  • 28
  • 38
  • 1
    Thinking outside the box - clever! You could also add a custom style to make the resizing "handle" look more like it is for resizing the column. – Yvonne Aburrow Jan 09 '19 at 15:48
  • dont see a scrollable tbody, for only this reason the next solution wins – Max Alexander Hanna Jan 11 '19 at 18:11
  • It works but theres a problem, the text area screws up any code you put in the header, that isnt quite the solution i needed. – Max Alexander Hanna Jan 11 '19 at 18:17
  • 1
    Resizing the text area is to resize the content of the cell; it can only grow the cell and thus the column. The text area can't shrink the width of the column because the content in other cells in the column makes it keep, as a minimum, its calculated share of the total table width. – Erik.J Jan 13 '19 at 11:50
  • 1
    ...comment continued: Also, the fixed height, restricting the text area to only horizontal resizing, needs a nowrap to avoid a line break misfitting its text content. – Erik.J Jan 13 '19 at 11:59
5

The only thing you really need is position: sticky; on the header th. This combined with your resize: horizontal will fix your issue if I understand your question right.

table {
  display: block;
  background-color: cyan;
  height: 200px !important;
  overflow-y: scroll;
}

thead th {
  position: sticky;
  top: 0;
  background: white;
  z-index: 10;
}

th,
td {
  resize: horizontal;
  overflow: auto;
  min-width: 25px;
}

td {
  font-size: 1.45vmin;
}

tbody {
  height: 30px !important;
  background-color: green;
  width: 100%;
}
<div style="height:30px;background-color:red;">
  <table>
    <thead>
      <tr>
        <th>header1</th>
        <th>header2</th>
        <th>header3</th>
        <th>header4</th>
        <th>header5</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>1</td>
        <td>2</td>
        <td>3</td>
        <td>4</td>
        <td>5</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>

    </tbody>
  </table>
</div>
Sølve T.
  • 4,159
  • 1
  • 20
  • 31
  • I think the block display on the table ruins the native width functionality of the table and it loses the ability to adapt to its content. – Erik.J Jan 12 '19 at 18:19
2

For the fixed header I used position: sticky and for the scroll I used a container to mimic the tbody scrolling (actually, it is the tbody that scrolls) and avoid a block display on the table or its tbody that would make it somewhat dysfunctional.

The sticky position has more support than resize anyway so with the mandatory resize property it's relatively safe crossbrowser.

https://caniuse.com/#search=sticky

https://caniuse.com/#search=resize

I tried several times editing this post to make the posted code run here on SO, but to no avail, so I made you a public JsFiddle to play with: https://jsfiddle.net/Erik_J/xzhpdqLo/

(@Sølve Tornøe, I wasn't aware of your post before I submitted. Was away for a few hours and missed to refresh before posting. So you were the first to give a valid solution.)

div {
    position: relative;
    margin: auto;
    outline: 1px solid #000;
    outline-offset: -1px;
    max-width: 900px;
    max-height: 300px;
    overflow-y: scroll;
}
table{ 
    width: 100%;
    border-collapse: collapse;
}
thead th { 
    position: -webkit-sticky;
    position: sticky;
    top: 0;
    resize: horizontal;
    overflow: auto;
    background: #cff;
}
th, td {
    padding: 5px 10px;
    border: 1px solid #000;
}
th {
    text-transform: capitalize;
}

<div>
    <table>
        <thead>
            <tr><th></th><th>header 1</th><th>header 2</th><th>header 3</th><th>header 4</th></tr>
        </thead>
        <tbody>
            <tr><th>header 1</th><td>1</td><td>2</td><td>3</td><td>4</td></tr>
            <tr><th>header 2 2222</th><td>1</td><td>2</td><td>3</td><td>4</td></tr>
            <tr><th>header 3</th><td>1</td><td>2</td><td>3</td><td>4</td></tr>
            <tr><th>header 4</th><td>1</td><td>2</td><td>3 3333333 3333333 3333333</td><td>4</td></tr>
            <tr><th>header 5</th><td>1</td><td>2</td><td>3</td><td>4</td></tr>
            <tr><th>header 6</th><td>1</td><td>2</td><td>3</td><td>4</td></tr>
            <tr><th>header 7</th><td>1</td><td>2</td><td>3</td><td>4</td></tr>
            <tr><th>header 8</th><td>1</td><td>2 2222 2222 2222</td><td>3</td><td>4</td></tr>
            <tr><th>header 9</th><td>1</td><td>2</td><td>3</td><td>4</td></tr>
            <tr><th>header 10</th><td>1</td><td>2</td><td>3</td><td>4</td></tr>
            <tr><th>header 11</th><td>1</td><td>2</td><td>3</td><td>4</td></tr>
            <tr><th>header 12</th><td>1</td><td>2</td><td>3</td><td>4</td></tr>
            <tr><th>header 13</th><td>1</td><td>2</td><td>3</td><td>4</td></tr>
            <tr><th>header 14</th><td>1</td><td>2</td><td>3</td><td>4</td></tr>
            <tr><th>header 15</th><td>1</td><td>2</td><td>3</td><td>4</td></tr>
            <tr><th>header 16</th><td>1</td><td>2</td><td>3</td><td>4</td></tr>
        </tbody>
    </table>
</div>
Erik.J
  • 586
  • 4
  • 7