How do I create an HTML table with a fixed/frozen left column and a scrollable body?
Asked Answered
F

30

398

I need a simple solution. I know it's similar to some other questions, like:

But I need just a single left column to be frozen and I would prefer a simple and script-less solution.

Fiske answered 21/8, 2009 at 14:17 Comment(0)
P
475

If you want a table where only the columns scroll horizontally, you can position: absolute the first column (and specify its width explicitly), and then wrap the entire table in an overflow-x: scroll block. Don't bother trying this in IE7, however...

Relevant HTML & CSS:

table {
  border-collapse: separate;
  border-spacing: 0;
  border-top: 1px solid grey;
}

td,
th {
  margin: 0;
  border: 1px solid grey;
  white-space: nowrap;
  border-top-width: 0px;
}

div {
  width: 500px;
  overflow-x: scroll;
  margin-left: 5em;
  overflow-y: visible;
  padding: 0;
}

.headcol {
  position: absolute;
  width: 5em;
  left: 0;
  top: auto;
  border-top-width: 1px;
  /*only relevant for first row*/
  margin-top: -1px;
  /*compensate for top border*/
}

.headcol:before {
  content: 'Row ';
}

.long {
  background: yellow;
  letter-spacing: 1em;
}
<div>
  <table>
    <tr>
      <th class="headcol">1</th>
      <td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
      <td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
    </tr>
    <tr>
      <th class="headcol">2</th>
      <td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
      <td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
    </tr>
    <tr>
      <th class="headcol">3</th>
      <td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
      <td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
    </tr>
    <tr>
      <th class="headcol">4</th>
      <td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
      <td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
    </tr>
    <tr>
      <th class="headcol">5</th>
      <td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
      <td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
    </tr>
    <tr>
      <th class="headcol">6</th>
      <td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
      <td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
    </tr>
  </table>
</div>

Fiddle

Parquet answered 21/8, 2009 at 15:31 Comment(30)
Note that getting the borders right (i.e., pixel perfectly aligned) is very tricky; in practice if you really want borders, I'd place them below the td level (by including all content of the td inside a div, for instance).Parquet
This doesn't handle arbitrary labels. Unless you have very predictably short labels, you will get this: jsfiddle.net/YMvk9/3724Smitty
@AaronLS: yep, you need to specify the width of the first column. If you need to deal with arbitrary-length labels, you could use text-overflow: ellipsis to cleanly display those.Parquet
@EamonNerbonne ellipsis was a good option I considered. Another option was to do something like this. Repeating the first column hidden as well as fixed. The second hidden column ensures the height of the rows syncs. Only works if you have a fixed width column though: jsfiddle.net/abkNM/2224Smitty
@Smitty I don't see how your approach works. In the jsfiddle you provided the first column does not have the same height as the following one. This becomes obvious if you replace the border-top by a border-bottom: jsfiddle.net/m4r73n/abkNM/4740Gibran
@Gibran You'll see it works fine with border-top. If you need to use border-bottom, then you will have to look for a different solution. If you find one that handles the various combinations of wrapping content well, then by all means please post it as an answer.Smitty
For anyone who is interested. Essentially the solution moves the headers off from the table and set each of them a 5em width. The table itself is pushed to the right for the same 5em width. This makes a visual appearance that the headers are still part of the normal flow in the table.Thinia
@EamonNerbonne having position: absolute on td and/or th causes width problemsWandis
@KashifKhan As demonstrated in the demo link, you can use td's with position absolute as long as you explicitly specify the width. If you're still having "width problems" you'll need to be more explicit about what those are.Parquet
@Smitty you should post your fiddle as an answer, this was exactly what I was looking for.Yenyenisei
@Eamon Nerbonne: how can set change height for fixed column based on content inside it ??Quidnunc
@Yogesh: because the first column is absolutely positioned, it won't automatically have the same height as the others. You'll need to explicitly size the tds of the exceptional row, e.g. .mySpecialHighRow > td { height: 3em; }Parquet
@Eamon Nerbonne: i can set height but my issue that, I have dynamic data in fixed column so i can't height explicitly. Is there any other way?? like calculating the height or something ??Quidnunc
@Yogesh: unfortunately not with this technique!Parquet
@EamonNerbonne can you suggest some other way. I am stuck at this. I am making this table using angular ng-repeat.Quidnunc
@Yogesh, since it's going to be quite a different solution, consider asking a new question with your specific needs. Alternatively, consider some of the other answers in this question, particularly those using javascript.Parquet
Unfortunately this does not work if the table has th tags.Hogen
@DeanPeterson the proof-of-concept does not include th tags, but the technique works with th tags too.Parquet
@DeanPeterson updated to use th's too, for illustration purposes.Parquet
sorry, I meant th tags inside a thead tagHogen
@EamonNerbonne : I want more than one columns (Left side) to be fixed. I tried to edit the fiddle, but couldn't succeed. Can you please help.Osteoplastic
Here is a smaller example without unnecessary code jsfiddle.net/kashesandr/rLv5b1ft/1Truax
This technique fails if row contains no data, right? I tried it on my table and set the first two columns on freeze mode, worked. But if one of the rows has no data it collapses on itself! the row above it and the one below it kind of squashes the empty row. Also I get some glitches when resizing the window (td background color changes to white in some of the td areas..!)Delk
@DeanPeterson It works with th inside a thead block. Just add the same .headcol class to the first such th.Elflock
Is there a solution for max-height of table? Then You need also vertical scroll. Works with mail table but not with fixed column. Is there a solution without javascript?Fullblown
I just did something similar to this, only to discover that it works great in all browsers except Firefox, which puts all the left column headers on top of each other next to the first row. It only does if the table is hidden and then shown; if it's visible all along, no problem.Mitigate
My first "sticky" column was covering the second column, so I had to add a thick border-left (roughly the width of the first column) to the second column ( td:nth-child(2) ). That got the second column pushed right enough to show fully.Meg
Here is also an option with flex that I whipped up based on Eamon Nerbonne great answer. Not sure how well it will work in the wild though. The upper left cell doesn't seem to work quite as well either. jsfiddle.net/65shyfxeLanded
@EamonNerbonne - I got this partially working, I have locked column1 using position:absolute;width:15em; however column2 now sits behind column1. When I scroll the rest of the colums they are scrolling behind colum1 and visibly pushing to the left of column1. What could be causing this?Korn
Like @HelloWorld mentioned above, due to left:0, this technique moves the first column to the left which messes with alignment. If you don't use left:0 then column2 starts behind column1. I found that if you do position:sticky;width:5em;left:0;z-index:1 that you get the desire effect EXCEPT the main table now scrolls behind the entire column 1 vs scolling disappearing at the front edge of column1. Its kind of a nice effect to.Korn
B
192

You can use sticky position. Here is a sample code. This is HTML/CSS solution. No js is required.

.view {
  margin: auto;
  width: 600px;
}

.wrapper {
  position: relative;
  overflow: auto;
  border: 1px solid black;
  white-space: nowrap;
}

.sticky-col {
  position: -webkit-sticky;
  position: sticky;
  background-color: white;
}

.first-col {
  width: 100px;
  min-width: 100px;
  max-width: 100px;
  left: 0px;
}

.second-col {
  width: 150px;
  min-width: 150px;
  max-width: 150px;
  left: 100px;
}
<div class="view">
  <div class="wrapper">
    <table class="table">
      <thead>
        <tr>
          <th class="sticky-col first-col">Number</th>
          <th class="sticky-col second-col">First Name</th>
          <th>Last Name</th>
          <th>Employer</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td class="sticky-col first-col">1</td>
          <td class="sticky-col second-col">Mark</td>
          <td>Ham</td>
          <td>Micro</td>
        </tr>
        <tr>
          <td class="sticky-col first-col">2</td>
          <td class="sticky-col second-col">Jacob</td>
          <td>Smith</td>
          <td>Adob Adob Adob AdobAdob Adob Adob Adob Adob</td>
        </tr>
        <tr>
          <td class="sticky-col first-col">3</td>
          <td class="sticky-col second-col">Larry</td>
          <td>Wen</td>
          <td>Goog Goog Goog GoogGoog Goog Goog Goog Goog Goog</td>
        </tr>
      </tbody>
    </table>
  </div>
</div>

codeply code: https://www.codeply.com/p/oZ4NjpvwbO

Bacteriology answered 28/11, 2018 at 18:52 Comment(15)
Sure, this is the easiest and the most logical way to do it, I did only: $("table th:first").css("position", "sticky").css("left", "0px").css("background-color", $("body").css("background-color")); and in loop for every row and that was it!Endometriosis
This should be the accepted answer. No position: absolute hacking and table cell sizes are not fixed.Nikko
is it possible to make only 1 column sticky? I am trying your link but i am unable to make it? Like i want to make employer column stickyDiaconicum
@MuneemHabib If you want only the first column to be sticky, see thisBacteriology
This is by far the best answer. Nothing is better than stickying the columns. Works great on Chrome, Safari and Firefox. Didn't test it on other browsers.Disposed
The solution works perfectly. The only issue is that the "non-sticky" columns are seen-through when scrolling horizontally. ibb.co/kJdHdpCBudgie
@AbishekH You can use cell border-color white.Bacteriology
@CircuitBreaker. Unfortunately, that didn't fix the issue. I am using an inset box-shadow on the right side. It looks a little better than having a border. Anyway, thank you for the solution, man. You saved me from hours of googling.Budgie
Great solution. Here is a Plunker based on this answer with fixed columns, fixed header, and non even row heights plnkr.co/plunk/l0m9pF3His2BrKjSMohammadmohammed
In December 2020, this still has very inconsistent cross-browser support. caniuse.com/css-stickySinatra
In Chrome and Firefox on Android 10 (on my Samsung M51), this only works for when the table is zoomed fully out, making it unsuitable for a large table because the text becomes tiny.Reynoso
Is this possible to do with width: auto;? If I don't know the width of my columns I can't use this left trick. Unless I start dabbling with js.Footing
@Melendowski, you need with for sticky columns.Bacteriology
Can I have different odd and even background-color with this solution?Expel
This does not work if you remove the border: 1px solid black; in the wrapper. Why?Seismo
C
57

For most browsers released after 2017:

You can use the position: sticky. See https://caniuse.com/#feat=css-sticky.

There is no need for a fixed width column.

Run the code snippet below to see how it works.

.tscroll {
  width: 400px;
  overflow-x: scroll;
  margin-bottom: 10px;
  border: solid black 1px;
}

.tscroll table td:first-child {
  position: sticky;
  left: 0;
  background-color: #ddd;
}

.tscroll td, .tscroll th {
  border-bottom: dashed #888 1px;
}
<html>
<div class="tscroll">
  <table>
    <thead>
      <tr>
        <th></th>
        <th colspan="5">Heading 1</th>
        <th colspan="8">Heading 2</th>
        <th colspan="4">Heading 3</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>9:00</td>
        <td>AAA</td>
        <td>BBB</td>
        <td>CCC</td>
        <td>DDD</td>
        <td>EEE</td>
        <td>FFF</td>
        <td>GGG</td>
        <td>HHH</td>
        <td>III</td>
        <td>JJJ</td>
        <td>KKK</td>
        <td>LLL</td>
        <td>MMM</td>
        <td>NNN</td>
        <td>OOO</td>
        <td>PPP</td>
        <td>QQQ</td>
      </tr>
      <tr>
        <td>10:00</td>
        <td>AAA</td>
        <td>BBB</td>
        <td>CCC</td>
        <td>DDD</td>
        <td>EEE</td>
        <td>FFF</td>
        <td>GGG</td>
        <td>HHH</td>
        <td>III</td>
        <td>JJJ</td>
        <td>KKK</td>
        <td>LLL</td>
        <td>MMM</td>
        <td>NNN</td>
        <td>OOO</td>
        <td>PPP</td>
        <td>QQQ</td>
      </tr>
      <tr>
        <td>11:00</td>
        <td>AAA</td>
        <td>BBB</td>
        <td>CCC</td>
        <td>DDD</td>
        <td>EEE</td>
        <td>FFF</td>
        <td>GGG</td>
        <td>HHH</td>
        <td>III</td>
        <td>JJJ</td>
        <td>KKK</td>
        <td>LLL</td>
        <td>MMM</td>
        <td>NNN</td>
        <td>OOO</td>
        <td>PPP</td>
        <td>QQQ</td>
      </tr>
      <tr>
        <td>12:00</td>
        <td>AAA</td>
        <td>BBB</td>
        <td>CCC</td>
        <td>DDD</td>
        <td>EEE</td>
        <td>FFF</td>
        <td>GGG</td>
        <td>HHH</td>
        <td>III</td>
        <td>JJJ</td>
        <td>KKK</td>
        <td>LLL</td>
        <td>MMM</td>
        <td>NNN</td>
        <td>OOO</td>
        <td>PPP</td>
        <td>QQQ</td>
      </tr>
      <tr>
        <td>13:00</td>
        <td>AAA</td>
        <td>BBB</td>
        <td>CCC</td>
        <td>DDD</td>
        <td>EEE</td>
        <td>FFF</td>
        <td>GGG</td>
        <td>HHH</td>
        <td>III</td>
        <td>JJJ</td>
        <td>KKK</td>
        <td>LLL</td>
        <td>MMM</td>
        <td>NNN</td>
        <td>OOO</td>
        <td>PPP</td>
        <td>QQQ</td>
      </tr>
      <tr>
        <td>14:00</td>
        <td>AAA</td>
        <td>BBB</td>
        <td>CCC</td>
        <td>DDD</td>
        <td>EEE</td>
        <td>FFF</td>
        <td>GGG</td>
        <td>HHH</td>
        <td>III</td>
        <td>JJJ</td>
        <td>KKK</td>
        <td>LLL</td>
        <td>MMM</td>
        <td>NNN</td>
        <td>OOO</td>
        <td>PPP</td>
        <td>QQQ</td>
      </tr>
      <tr>
        <td>15:00</td>
        <td>AAA</td>
        <td>BBB</td>
        <td>CCC</td>
        <td>DDD</td>
        <td>EEE</td>
        <td>FFF</td>
        <td>GGG</td>
        <td>HHH</td>
        <td>III</td>
        <td>JJJ</td>
        <td>KKK</td>
        <td>LLL</td>
        <td>MMM</td>
        <td>NNN</td>
        <td>OOO</td>
        <td>PPP</td>
        <td>QQQ</td>
      </tr>
      <tr>
        <td>16:00</td>
        <td>AAA</td>
        <td>BBB</td>
        <td>CCC</td>
        <td>DDD</td>
        <td>EEE</td>
        <td>FFF</td>
        <td>GGG</td>
        <td>HHH</td>
        <td>III</td>
        <td>JJJ</td>
        <td>KKK</td>
        <td>LLL</td>
        <td>MMM</td>
        <td>NNN</td>
        <td>OOO</td>
        <td>PPP</td>
        <td>QQQ</td>
      </tr>
      <tr>
        <td>17:00</td>
        <td>AAA</td>
        <td>BBB</td>
        <td>CCC</td>
        <td>DDD</td>
        <td>EEE</td>
        <td>FFF</td>
        <td>GGG</td>
        <td>HHH</td>
        <td>III</td>
        <td>JJJ</td>
        <td>KKK</td>
        <td>LLL</td>
        <td>MMM</td>
        <td>NNN</td>
        <td>OOO</td>
        <td>PPP</td>
        <td>QQQ</td>
      </tr>
    </tbody>
    </table>
</div>
Chainey answered 24/8, 2019 at 16:25 Comment(11)
Already exist an answer suggestion sticky, so no need for one moreCurzon
@LGSon I disagree, there were only two other answers using sticky. One is using bootstrap CSS to implement sticky. The other answer provides code not directly related to the question, is incorrect, and uses a fixed width for the sticky column.Chainey
Whether one use Bootstrap and one use fix width, doesn't make position: sticky change in its behavior, hence using it already been shown.Curzon
Doing away with fixed with is of interest to me. However, I don't see how this solution could scale to 2+ sticky columns without fixed width for the sticky columns coming into play, since it seems we need an accurate left property on all sticky columns.Aventurine
@Aventurine There are several ways to deal with this depending on your situation. If the first column has a known width, then set the left property on the second column to be the width of the first column. If that does not work for you, a nested table should work. If you don't like the idea of a nested table, you can also use nested divs with table-like properties.Chainey
It's a pity that you have to scroll the vertical bar to see the horizontal barGerena
@Gerena If you know the table is scrollable, you can scroll with mouse or trackpad without using the horizontal bar.Chainey
Well, you have to use Shift+Mousescroll to scroll left-right, which almost nobody knows. I've implemented a solution which solves this, but it requires some jQuery, which doesn't meet the requirements of the original post.Gerena
@Gerena True enough, I am now mostly using the Magic Mouse 2 which allows you to scroll both horizontally and vertically by swiping your finger. It is the default mouse for Mac, but you can install the drivers on Windows for scrolling to work there as well. Not a solution for everyone, but worth a mention anyhow. The only annoyance is you can't use it and charge it at the same time.Chainey
@Savage, I for one did not know about the Shift+Mousescroll trick, but now I do thanks to you!Durware
I like this answer better because it doesn't require any additional classes to target the table. This is useful when we don't have control over the HTMLTownshend
C
27

This is an interesting jQuery plugin that creates fixed headers and/or columns. Toggle fixed column to be true on the demo page to see it in action.

Clip answered 4/1, 2011 at 15:31 Comment(8)
this is for rows, not columnsNyberg
@Fortega: It is also for columns, if you set the property fixedColumn: true. There is a demo you can check out.Clip
ah ok, that was not clear. The demo was not working last week. But it is now.Nyberg
is this plug in also working in jQuery 1.9? I've problems. Error: TypeError: $.browser is undefinedLabroid
@Labroid You should use the Migrate Plugin from jQuery if a plugin does not work with 1.9.x -> jquery.com/downloadDiadromous
This plugin is really slow with large tables.Anschluss
Thanks, In myTheme.css if this code will be changed like bellow ( adding min-width: 80px; max-width: 80px; ) it would be better; the table headers will be same width with columns. Some times the column and headers are not equal for this library. .fancyTable td, .fancyTable th { /* appearance / border: 1px solid #778899; / size */ padding: 5px; min-width: 80px; max-width: 80px; white-space: normal; word-wrap: break-word; }Trompe
Doesn't work. Even example on demo page can't have fixed columnCaldera
A
20

In case of fixed width left column the best solution is provided by Eamon Nerbonne.

In case of variable width left column the best solution I found is to make two identical tables and push one above another. Demo: http://jsfiddle.net/xG5QH/6/.

<!DOCTYPE html>
<html>
<head>
<style type="text/css">
/* important styles */

.container {
   /* Attach fixed-th-table to this container,
      in order to layout fixed-th-table
      in the same way as scolled-td-table" */
   position: relative;

   /* Truncate fixed-th-table */
   overflow: hidden;
}

.fixed-th-table-wrapper td,
.fixed-th-table-wrapper th,
.scrolled-td-table-wrapper td,
.scrolled-td-table-wrapper th {
   /* Set background to non-transparent color
      because two tables are one above another.
    */
   background: white;
}
.fixed-th-table-wrapper {
   /* Make table out of flow */
   position: absolute;
}
.fixed-th-table-wrapper th {
    /* Place fixed-th-table th-cells above 
       scrolled-td-table td-cells.
     */
    position: relative;
    z-index: 1;
}
.scrolled-td-table-wrapper td {
    /* Place scrolled-td-table td-cells
       above fixed-th-table.
     */
    position: relative;
}
.scrolled-td-table-wrapper {
   /* Make horizonal scrollbar if needed */
   overflow-x: auto;
}


/* Simulating border-collapse: collapse,
   because fixed-th-table borders
   are below ".scrolling-td-wrapper table" borders
*/

table {
    border-spacing: 0;
}
td, th {
   border-style: solid;
   border-color: black;
   border-width: 1px 1px 0 0;
}
th:first-child {
   border-left-width: 1px;
}
tr:last-child td,
tr:last-child th {
   border-bottom-width: 1px;
}

/* Unimportant styles */

.container {
    width: 250px;
}
td, th {
   padding: 5px;
}
</style>
</head>

<body>
<div class="container">

<div class="fixed-th-table-wrapper">
<!-- fixed-th-table -->
<table>
    <tr>
         <th>aaaaaaa</th>
         <td>ccccccccccc asdsad asd as</td>
         <td>ccccccccccc asdsad asd as</td>
    </tr>
    <tr>
         <th>cccccccc</th>
         <td>xxxxxxxxxxxxxxxxxxxx yyyyyyyyyyyyyy zzzzzzzzzzzzz</td>
         <td>xxxxxxxxxxxxxxxxxxxx yyyyyyyyyyyyyy zzzzzzzzzzzzz</td>
    </tr>
</table>
</div>

<div class="scrolled-td-table-wrapper">
<!-- scrolled-td-table
     - same as fixed-th-table -->
<table>
    <tr>
         <th>aaaaaaa</th>
         <td>ccccccccccc asdsad asd as</td>
         <td>ccccccccccc asdsad asd as</td>
    </tr>
    <tr>
         <th>cccccccc</th>
         <td>xxxxxxxxxxxxxxxxxxxx yyyyyyyyyyyyyy zzzzzzzzzzzzz</td>
         <td>xxxxxxxxxxxxxxxxxxxx yyyyyyyyyyyyyy zzzzzzzzzzzzz</td>
    </tr>
</table>
</div>

</div>
</body>
</html>
Araxes answered 9/7, 2013 at 20:42 Comment(2)
+10 very clever. Had to set border-collapse to seperate under bootstap, other wise the borders don't float with the cells. Bit of border cleaning required after that.Hunterhunting
Very Nice! Thank You! i didn't use <th> for my own reasons, i used a class to indicate which column were to be fixed, and, for good measure, added a visibility: collapse; to the columns to hide from the fixed table (gotta love the :not() css selector!). i also used jQuery to .clone() the table, once it has been generated by php+MySQL+ajax, and insert it into a cleaned out div...Erleena
C
17

You can just make the first column position: sticky; z-index: 9. It will make the column/row stick to its current position. Checkout my example codepen here https://codepen.io/swastikmishra/pen/zYYdKBQ

HTML Example

table {
  text-align: center;
}

.table-container {
  width: 500px;
  height: 300px;
  overflow: scroll;
}

table th,
table td {
  white-space: nowrap;
  padding: 10px 20px;
  font-family: Arial;
}

table tr th:first-child,
table td:first-child {
  position: sticky;
  width: 100px;
  left: 0;
  z-index: 10;
  background: #fff;
}

table tr th:first-child {
  z-index: 11;
}

table tr th {
  position: sticky;
  top: 0;
  z-index: 9;
  background: #fff;
}
<div class="table-container">
  <table>
    <tr>
      <th>Hello World</th>
      <th>Hello World</th>
      <th>Hello World</th>
      <th>Hello World</th>
      <th>Hello World</th>
      <th>Hello World</th>
      <th>Hello World</th>
    </tr>
    <tr>
      <td>H11</td>
      <td>H12</td>
      <td>H13</td>
      <td>H14</td>
      <td>H15</td>
      <td>H16</td>
      <td>H17</td>
    </tr>
    <tr>
      <td>H21</td>
      <td>H22</td>
      <td>H23</td>
      <td>H24</td>
      <td>H25</td>
      <td>H26</td>
      <td>H27</td>
    </tr>
    <tr>
      <td>H31</td>
      <td>H32</td>
      <td>H33</td>
      <td>H34</td>
      <td>H35</td>
      <td>H36</td>
      <td>H37</td>
    </tr>
    <tr>
      <td>H41</td>
      <td>H42</td>
      <td>H44</td>
      <td>H44</td>
      <td>H45</td>
      <td>H46</td>
      <td>H47</td>
    </tr>
    <tr>
      <td>H51</td>
      <td>H52</td>
      <td>H54</td>
      <td>H54</td>
      <td>H55</td>
      <td>H56</td>
      <td>H57</td>
    </tr>
    <tr>
      <td>H61</td>
      <td>H62</td>
      <td>H64</td>
      <td>H64</td>
      <td>H65</td>
      <td>H66</td>
      <td>H67</td>
    </tr>
    <tr>
      <td>H71</td>
      <td>H72</td>
      <td>H74</td>
      <td>H74</td>
      <td>H75</td>
      <td>H76</td>
      <td>H77</td>
    </tr>
    <tr>
      <td>H81</td>
      <td>H82</td>
      <td>H84</td>
      <td>H84</td>
      <td>H85</td>
      <td>H86</td>
      <td>H87</td>
    </tr>
  </table>
</div>
Cantonment answered 29/10, 2019 at 3:45 Comment(0)
C
15

A little late but I did run across this thread when trying out solutions for myself. Assuming you're using modern browsers nowadays, I came up with a solution using CSS calc() to help guarantee widths met up.

.table-fixed-left table,
.table-fixed-right table {
  border-collapse: collapse;
}
.table-fixed-right td,
.table-fixed-right th,
.table-fixed-left td,
.table-fixed-left th {
  border: 1px solid #ddd;
  padding: 5px 5px;
}
.table-fixed-left {
  width: 120px;
  float: left;
  position: fixed;
  overflow-x: scroll;
  white-space: nowrap;
  text-align: left;
  border: 1px solid #ddd;
  z-index: 2;
}
.table-fixed-right {
  width: calc(100% - 145px);
  right: 15px;
  position: fixed;
  overflow-x: scroll;
  border: 1px solid #ddd;
  white-space: nowrap;
}
.table-fixed-right td,
.table-fixed-right th {
  padding: 5px 10px;
}
<div class="table-fixed-left">
  <table>
    <tr>
      <th>Normal Header</th>
    </tr>
    <tr>
      <th>Header with extra line
        <br/>&nbsp;</th>
    </tr>
    <tr>
      <th>Normal Header</th>
    </tr>
    <tr>
      <th>Normal with extra line
        <br/>&nbsp;</th>
    </tr>
    <tr>
      <th>Normal Header</th>
    </tr>
    <tr>
      <th>Normal Header</th>
    </tr>
  </table>
</div>
<div class="table-fixed-right">
  <table>
    <tr>
      <th>Header</th>
      <th>Another header</th>
      <th>Header</th>
      <th>Header really really really really long</th>
    </tr>
    <tr>
      <td>Info Long</td>
      <td>Info
        <br/>with second line</td>
      <td>Info
        <br/>with second line</td>
      <td>Info Long</td>
    </tr>
    <tr>
      <td>Info Long</td>
      <td>Info Long</td>
      <td>Info Long</td>
      <td>Info Long</td>
    </tr>
    <tr>
      <td>Info
        <br/>with second line</td>
      <td>Info
        <br/>with second line</td>
      <td>Info
        <br/>with second line</td>
      <td>Info</td>
    </tr>
    <tr>
      <td>Info</td>
      <td>Info</td>
      <td>Info</td>
      <td>Info</td>
    </tr>
    <tr>
      <td>Info</td>
      <td>Info</td>
      <td>Info</td>
      <td>Info</td>
    </tr>
  </table>
</div>

Hope this helps someone!

Collegium answered 10/5, 2015 at 20:37 Comment(0)
H
12

Style the left column with position: fixed. (You'll presumably want to use top and left styles to control where exactly it occurs.)

Hatch answered 21/8, 2009 at 14:20 Comment(2)
Yeah; you should skip the whole "table" part and just use two divs, one for the left column and one for the rest of the content. Left column div gets position: fixed and stays put, rest of content acts normally (presumably with a left margin set so it doesn't overlap the left column).Hatch
This won't work if you're looking for generic styling for one or more tables that can appear at arbitrary positions on a page, i.e. if your design is responsive.Pyxie
T
9

I took Earmon Nerbonne's answer and edited it to work with tables that fill the whole width.

http://jsfiddle.net/DYgD6/6/

<!DOCTYPE html>
<html><head><title>testdoc</title>
<style type="text/css">
            body {
        font:16px Calibri;
    }
    table {
        border-collapse:separate;
        border-top: 3px solid grey;
    }
    td {
        margin:0;
        border:3px solid grey;
        border-top-width:0px;
        white-space:nowrap;
    }
    #outerdiv {
        position: absolute;
        top: 0;
        left: 0;
        right: 5em;
    }
    #innerdiv {
        width: 100%;
        overflow-x:scroll;
        margin-left: 5em;
        overflow-y:visible;
        padding-bottom:1px;
    }
    .headcol {
        position:absolute;
        width:5em;
        left:0;
        top:auto;
        border-right: 0px none black;
        border-top-width:3px;
        /*only relevant for first row*/
        margin-top:-3px;
        /*compensate for top border*/
    }
    .headcol:before {
        content:'Row ';
    }
    .long {
        background:yellow;
        letter-spacing:1em;
    }
</style></head><body>
  <div id="outerdiv">
   <div id="innerdiv">
    <table>
        <tr>
            <td class="headcol">1</td>
            <td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
            <td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
        </tr>
        <tr>
            <td class="headcol">2</td>
            <td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
            <td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
        </tr>
        <tr>
            <td class="headcol">3</td>
            <td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
            <td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
        </tr>
        <tr>
            <td class="headcol">4</td>
            <td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
            <td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
        </tr>
        <tr>
            <td class="headcol">5</td>
            <td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
            <td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
        </tr>
        <tr>
            <td class="headcol">6</td>
            <td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
            <td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
        </tr>
        <tr>
            <td class="headcol">7</td>
            <td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
            <td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
        </tr>
        <tr>
            <td class="headcol">8</td>
            <td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
            <td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
        </tr>
        <tr>
            <td class="headcol">9</td>
            <td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
            <td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
        </tr>
    </table>
</div></div>
</body></html>

The width of the fixed column still needs to be a set value though.

Tautonym answered 25/5, 2013 at 11:16 Comment(0)
T
8

If you're developing something more complicated and want multiple columns to be fixed/stuck to the left, you'll probably need something like this.

.wrapper {
    overflow-x: scroll;
}

td {
    min-width: 50px;
}

.fixed {
    position: absolute;
    background: #aaa;
}
<div class="content" style="width: 400px">

  <div class="wrapper" style="margin-left: 100px">

      <table>
        <thead>
          <tr>
            <th class="fixed" style="left: 0px">aaa</th>
            <th class="fixed" style="left: 50px">aaa2</th>
            <th>a</th>
            <th>b</th>
            <th>c</th>
            <th>d</th>
            <th>e</th>
            <th>f</th>
            <th>a</th>
            <th>b</th>
            <th>c</th>
            <th>d</th>
            <th>e</th>
            <th>f</th>
            <th>a</th>
            <th>b</th>
            <th>c</th>
            <th>d</th>
            <th>e</th>
            <th>f</th>
            <th>a</th>
            <th>b</th>
            <th>c</th>
            <th>d</th>
            <th>e</th>
            <th>f</th>        
          </tr>
        </thead>
        <tbody>
          <tr>
            <td class="fixed" style="left: 0px">aaa</td>
            <td class="fixed" style="left: 50px">aaa2</td>
            <td>a</td>
            <td>b</td>
            <td>c</td>
            <td>d</td>
            <td>e</td>
            <td>f</td>
            <td>a</td>
            <td>b</td>
            <td>c</td>
            <td>d</td>
            <td>e</td>
            <td>f</td>
            <td>a</td>
            <td>b</td>
            <td>c</td>
            <td>d</td>
            <td>e</td>
            <td>f</td>
            <td>a</td>
            <td>b</td>
            <td>c</td>
            <td>d</td>
            <td>e</td>
            <td>f</td>
          </tr>
          <tr>
            <td class="fixed" style="left: 0">bbb</td>
            <td class="fixed" style="left: 50px">bbb2</td>
            <td>a</td>
            <td>b</td>
            <td>c</td>
            <td>d</td>
            <td>e</td>
            <td>f</td>
            <td>a</td>
            <td>b</td>
            <td>c</td>
            <td>d</td>
            <td>e</td>
            <td>f</td>
            <td>a</td>
            <td>b</td>
            <td>c</td>
            <td>d</td>
            <td>e</td>
            <td>f</td>
            <td>a</td>
            <td>b</td>
            <td>c</td>
            <td>d</td>
            <td>e</td>
            <td>f</td>
          </tr>
        </tbody>
      </table>

  </div>

</div>
Truax answered 3/3, 2017 at 12:30 Comment(1)
This works great but how can I set a fixed heigth for the table?Haigh
F
3

If you're in Webdevelopper hell and need to make this work for IE6, here's a sample code I used:

<html>
<head>
<style type="text/css">
.fixme {
    position: relative;
    left: expression( ( 20 + ( ignoreMe2 = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft ) ) + 'px' );
    background-color: #FFFFFF;
}
</style>
</head>
<body>
<table width="1500px" border="2">
    <tr>
        <td class="fixme" style="width: 200px;">loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet</td>
        <td>loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet </td>
        <td>loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet </td>
        <td>loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet </td>
    </tr>
    <tr>
        <td class="fixme" style="width: 200px;">loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet </td>
        <td>loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet </td>
        <td>loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet </td>
        <td>loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet </td>
    </tr>
    <tr>
        <td class="fixme" style="width: 200px;">loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet </td>
        <td>loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet </td>
        <td>loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet </td>
        <td>loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet </td>
    </tr>
    <tr>
        <td class="fixme" style="width: 200px;">loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet </td>
        <td>loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet </td>
        <td>loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet </td>
        <td>loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet loremp ispum dolor sit amet </td>
    </tr>
</table>
</body>
</html>

This will work probably ONLY for IE6, so use conditional comments for the CSS.

Feinberg answered 8/12, 2011 at 14:59 Comment(0)
H
3
.div1 {
    width: 600px;
    height: 400px;
    overflow: scroll;
    border: 1px solid #777777;
}

.div1 table {
    border-spacing: 0;
}

.div1 th {
    border-left: none;
    border-right: 1px solid #bbbbbb;
    padding: 5px;
    width: 80px;
    min-width: 80px;
    position: sticky;
    top: 0;
    background: #727272;
    color: #e0e0e0;
    font-weight: normal;
}

.div1 td {
    border-left: none;
    border-right: 1px solid #bbbbbb;
    border-bottom: 1px solid #bbbbbb;
    padding: 5px;
    width: 80px;
    min-width: 80px;
}

.div1 th:nth-child(1),
.div1 td:nth-child(1) {
    position: sticky;
    left: 0;
    width: 150px;
    min-width: 150px;
}

.div1 th:nth-child(2),
.div1 td:nth-child(2) {
    position: sticky;
    /* 1st cell left/right padding + 1st cell width + 1st cell left/right border width */
    /* 0 + 5 + 150 + 5 + 1 */
    left: 161px;
    width: 50px;
    min-width: 50px;
}

.div1 td:nth-child(1),
.div1 td:nth-child(2) {
    background: #ffebb5;
}

.div1 th:nth-child(1),
.div1 th:nth-child(2) {
    z-index: 2;
}

the HTML,

<div class="div1">
    <table>
        <tr>
            <th>Column 1</th>
            <th>Column 2</th>
            <th>Column 3</th>
            <th>Column 4</th>
            <th>Column 5</th>
            <th>Column 6</th>
        </tr>
        <tr>
            <td>Row Data 1</td>
            <td>Row Data 2</td>
            <td>Row Data 3</td>
            <td>Row Data 4</td>
            <td>Row Data 5</td>
            <td>Row Data 6</td>
        </tr>
        <tr>
            <td>Row Data 1</td>
            <td>Row Data 2</td>
            <td>Row Data 3</td>
            <td>Row Data 4</td>
            <td>Row Data 5</td>
            <td>Row Data 6</td>
        </tr>
    </table>
</div>

Youtube Video Walkthrough: https://www.youtube.com/watch?v=_dpSEjaKqSE

Example 1: Fixed width & height

https://adriancs.com/demo/freeze_table_column_row.html

Example 2: Responsive width & height (adjusted by using viewport)

https://adriancs.com/html-css-js/298/html-table-freeze-row-and-column-with-css/

Full tutorial explanation:

https://adriancs.com/demo/freeze_table_column_row_responsive_viewport.html

Hayley answered 15/12, 2021 at 15:52 Comment(0)
M
3

.container {
    width: 500px;
    height: 300px;
    background-color: #ddd;
    overflow: auto;
    border: 1px solid #ccc;
}
table {
  table-layout: fixed;
  width: 100%;
  overflow-x: scroll;
    border-collapse: collapse;
}

td,th {
    border: 1px solid #ccc;
}
th {
    font-weight: 600;
    text-align: left;
    background-color: #f1f4f7;
}
.fixed-td {
    position: sticky;
    width: 100px;
    z-index: 2;
    left: 0;
    background-color: #fff;
}
.fixed-hd {
    position: sticky;
    top: 0;
    z-index: 1;
}
.left-top-td {
    z-index: 3;
}
.scrollable-td {
    width: 200px;
}
<div class="container">
    <table>
        <tr>
            <th class="fixed-td fixed-hd left-top-td">Fixed</td>
            <th class="scrollable-td fixed-hd">Scrollable 1</td>
            <th class="scrollable-td fixed-hd">Scrollable 2</td>
            <th class="scrollable-td fixed-hd">Scrollable 3</td>
            <th class="scrollable-td fixed-hd">Scrollable 4</td>
        </tr>
        <tr>
            <td class="fixed-td">Fixed</td>
            <td class="scrollable-td">Scrollable 1</td>
            <td class="scrollable-td">Scrollable 2</td>
            <td class="scrollable-td">Scrollable 3</td>
            <td class="scrollable-td">Scrollable 4</td>
        </tr>
        <tr>
            <td class="fixed-td">Fixed</td>
            <td class="scrollable-td">Scrollable 1</td>
            <td class="scrollable-td">Scrollable 2</td>
            <td class="scrollable-td">Scrollable 3</td>
            <td class="scrollable-td">Scrollable 4</td>
        </tr>
        <tr>
            <td class="fixed-td">Fixed</td>
            <td class="scrollable-td">Scrollable 1</td>
            <td class="scrollable-td">Scrollable 2</td>
            <td class="scrollable-td">Scrollable 3</td>
            <td class="scrollable-td">Scrollable 4</td>
        </tr>
        <tr>
            <td class="fixed-td">Fixed</td>
            <td class="scrollable-td">Scrollable 1</td>
            <td class="scrollable-td">Scrollable 2</td>
            <td class="scrollable-td">Scrollable 3</td>
            <td class="scrollable-td">Scrollable 4</td>
        </tr>
        <tr>
            <td class="fixed-td">Fixed</td>
            <td class="scrollable-td">Scrollable 1</td>
            <td class="scrollable-td">Scrollable 2</td>
            <td class="scrollable-td">Scrollable 3</td>
            <td class="scrollable-td">Scrollable 4</td>
        </tr>
        <tr>
            <td class="fixed-td">Fixed</td>
            <td class="scrollable-td">Scrollable 1</td>
            <td class="scrollable-td">Scrollable 2</td>
            <td class="scrollable-td">Scrollable 3</td>
            <td class="scrollable-td">Scrollable 4</td>
        </tr>
        <tr>
            <td class="fixed-td">Fixed</td>
            <td class="scrollable-td">Scrollable 1</td>
            <td class="scrollable-td">Scrollable 2</td>
            <td class="scrollable-td">Scrollable 3</td>
            <td class="scrollable-td">Scrollable 4</td>
        </tr>
        <tr>
            <td class="fixed-td">Fixed</td>
            <td class="scrollable-td">Scrollable 1</td>
            <td class="scrollable-td">Scrollable 2</td>
            <td class="scrollable-td">Scrollable 3</td>
            <td class="scrollable-td">Scrollable 4</td>
        </tr>
        <tr>
            <td class="fixed-td">Fixed</td>
            <td class="scrollable-td">Scrollable 1</td>
            <td class="scrollable-td">Scrollable 2</td>
            <td class="scrollable-td">Scrollable 3</td>
            <td class="scrollable-td">Scrollable 4</td>
        </tr>
        <tr>
            <td class="fixed-td">Fixed</td>
            <td class="scrollable-td">Scrollable 1</td>
            <td class="scrollable-td">Scrollable 2</td>
            <td class="scrollable-td">Scrollable 3</td>
            <td class="scrollable-td">Scrollable 4</td>
        </tr>
        <tr>
            <td class="fixed-td">Fixed</td>
            <td class="scrollable-td">Scrollable 1</td>
            <td class="scrollable-td">Scrollable 2</td>
            <td class="scrollable-td">Scrollable 3</td>
            <td class="scrollable-td">Scrollable 4</td>
        </tr>
        <tr>
            <td class="fixed-td">Fixed</td>
            <td class="scrollable-td">Scrollable 1</td>
            <td class="scrollable-td">Scrollable 2</td>
            <td class="scrollable-td">Scrollable 3</td>
            <td class="scrollable-td">Scrollable 4</td>
        </tr>
        <tr>
            <td class="fixed-td">Fixed</td>
            <td class="scrollable-td">Scrollable 1</td>
            <td class="scrollable-td">Scrollable 2</td>
            <td class="scrollable-td">Scrollable 3</td>
            <td class="scrollable-td">Scrollable 4</td>
        </tr>
        <tr>
            <td class="fixed-td">Fixed</td>
            <td class="scrollable-td">Scrollable 1</td>
            <td class="scrollable-td">Scrollable 2</td>
            <td class="scrollable-td">Scrollable 3</td>
            <td class="scrollable-td">Scrollable 4</td>
        </tr>
        <tr>
            <td class="fixed-td">Fixed</td>
            <td class="scrollable-td">Scrollable 1</td>
            <td class="scrollable-td">Scrollable 2</td>
            <td class="scrollable-td">Scrollable 3</td>
            <td class="scrollable-td">Scrollable 4</td>
        </tr>
        <tr>
            <td class="fixed-td">Fixed</td>
            <td class="scrollable-td">Scrollable 1</td>
            <td class="scrollable-td">Scrollable 2</td>
            <td class="scrollable-td">Scrollable 3</td>
            <td class="scrollable-td">Scrollable 4</td>
        </tr>
        <tr>
            <td class="fixed-td">Fixed last</td>
            <td class="scrollable-td">Scrollable 1</td>
            <td class="scrollable-td">Scrollable 2</td>
            <td class="scrollable-td">Scrollable 3</td>
            <td class="scrollable-td">Scrollable last</td>
        </tr>
    </table>
 </div>
Mouthpart answered 28/6, 2023 at 11:30 Comment(2)
Thanks. This works perfectly. Mr Dilip, could you add some explanations? I can help if you don't mind.-Eboat
yes please, if you can,Mouthpart
F
2

No need to add any plugin, CSS can do this job !!!

The idea is to make the position of all the first cells in each column absolute, and make width fixed. Ex:

max-width: 125px;
min-width: 125px;
position: absolute;

This hides some parts of some columns under the first column, so add an empty second column (add second empty td) with width same as the first column.

I tested and this works in Chrome and Firefox.

Freund answered 31/1, 2014 at 6:56 Comment(3)
I tried this and it works in IE8 as well. But having this, how can I make the first row also fixed. I tried it but because I set the left column absolute to have it frozen, this is affecting when making the first row frozen. Please help.....As I noticed, either we make first row frozen or first column frozen, but making both frozen does not work.....Underdone
Could you please provide an example?Truax
This solution works like a charm but messing the column data if there is a pagination in the table while scrolling verticallyRamiroramjet
H
2

Eamon Nerbonne, I changed some css in your code and it's better now(the scroll bar starts from the first row)

http://jsfiddle.net/At8L8/

I just add two line :

.div : padding-left:5em;
.headcol : background-color : #fff;
Harl answered 25/7, 2014 at 8:30 Comment(1)
Can you please tell me how you got the scroll bar start from frozen column?? Also does changing the <th> to <td> have any impact?Leonteen
J
2

Here is another modification of the most popular answer, but with handling of variable length of text in the first column labels: http://jsfiddle.net/ozx56n41/

Basically, I'm using the second column for creating row height, like was mentioned. But my fiddle actually works unlike most mentioned above.

HTML:

<div id="outerdiv">
    <div id="innerdiv">
        <table>
            <tr>
                <td class="headcol"><div>This is a long label</div></td>
                <td class="hiddenheadcol"><div>This is a long label</div></td>
                <td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
                <td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
            </tr>
            <tr>
                <td class="headcol"><div>Short label</div></td>
                <td class="hiddenheadcol"><div>Short label</div></td>
                <td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
                <td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
            </tr>
        </table>
    </div>
</div>

CSS:

body {
    font: 16px Calibri;
}
#outerdiv {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    width: 100%;
    border-top: 1px solid grey;
}
#innerdiv {
    overflow-x: scroll;
    margin-left: 100px;
    overflow-y: visible;
    padding-bottom: 1px;
}
table {
    border-collapse:separate;
}
td {
    margin: 0;
    border: 1px solid grey;
    border-top-width: 0;
    border-left-width: 0px;
    padding: 10px;
}
td.headcol {
    /* Frozen 1st column */
    position: absolute;
    left: 0;
    top: auto;
    border-bottom-width: 1px;
    padding: 0;
    border-left-width: 1px;
}
td.hiddenheadcol {
    /* Hidden 2nd column to create height */
    max-width: 0;
    visibility: hidden;
    padding: 0;
}
td.headcol div {
    /* Text container in the 1st column */
    width: 100px;
    max-width: 100px;
    background: lightblue;
    padding: 10px;
    box-sizing: border-box;
}
td.hiddenheadcol div {
    /* Text container in the 2nd column */
    width: 100px;
    max-width: 100px;
    background: red;
    padding: 10px;
}
td.long {
    background:yellow;
    letter-spacing:1em;
}
Jeggar answered 6/6, 2017 at 3:34 Comment(0)
S
2

For me this was the only one that worked perfectly (thanks to Paul O'Brien!): https://codepen.io/paulobrien/pen/gWoVzN

Here's the snippet:

// requires jquery library
jQuery(document).ready(function() {
  jQuery(".main-table").clone(true).appendTo('#table-scroll').addClass('clone');   
 });
  .table-scroll {
    position:relative;
    max-width:600px;
    margin:auto;
    overflow:hidden;
    border:1px solid #000;
  }
.table-wrap {
	width:100%;
	overflow:auto;
}
.table-scroll table {
	width:100%;
	margin:auto;
	border-collapse:separate;
	border-spacing:0;
}
.table-scroll th, .table-scroll td {
	padding:5px 10px;
	border:1px solid #000;
	background:#fff;
	white-space:nowrap;
	vertical-align:top;
}
.table-scroll thead, .table-scroll tfoot {
	background:#f9f9f9;
}
.clone {
	position:absolute;
	top:0;
	left:0;
	pointer-events:none;
}
.clone th, .clone td {
	visibility:hidden
}
.clone td, .clone th {
	border-color:transparent
}
.clone tbody th {
	visibility:visible;
	color:red;
}
.clone .fixed-side {
	border:1px solid #000;
	background:#eee;
	visibility:visible;
}
.clone thead, .clone tfoot{background:transparent;}
<div id="table-scroll" class="table-scroll">
  <div class="table-wrap">
    <table class="main-table">
      <thead>
        <tr>
          <th class="fixed-side" scope="col">&nbsp;</th>
          <th scope="col">Header 2</th>
          <th scope="col">Header 3</th>
          <th scope="col">Header 4</th>
          <th scope="col">Header 5</th>
          <th scope="col">Header 6</th>
          <th scope="col">Header 7</th>
          <th scope="col">Header 8</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th class="fixed-side">Left Column</th>
          <td>Cell content<br>
            test</td>
          <td><a href="#">Cell content longer</a></td>
          <td>Cell content</td>
          <td>Cell content</td>
          <td>Cell content</td>
          <td>Cell content</td>
          <td>Cell content</td>
        </tr>
        <tr>
          <th class="fixed-side">Left Column</th>
          <td>Cell content</td>
          <td>Cell content longer</td>
          <td>Cell content</td>
          <td>Cell content</td>
          <td>Cell content</td>
          <td>Cell content</td>
          <td>Cell content</td>
        </tr>
        <tr>
          <th class="fixed-side">Left Column</th>
          <td>Cell content</td>
          <td>Cell content longer</td>
          <td>Cell content</td>
          <td>Cell content</td>
          <td>Cell content</td>
          <td>Cell content</td>
          <td>Cell content</td>
        </tr>
        <tr>
          <th class="fixed-side">Left Column</th>
          <td>Cell content</td>
          <td>Cell content longer</td>
          <td>Cell content</td>
          <td>Cell content</td>
          <td>Cell content</td>
          <td>Cell content</td>
          <td>Cell content</td>
        </tr>
        <tr>
          <th class="fixed-side">Left Column</th>
          <td>Cell content</td>
          <td>Cell content longer</td>
          <td>Cell content</td>
          <td>Cell content</td>
          <td>Cell content</td>
          <td>Cell content</td>
          <td>Cell content</td>
        </tr>
        <tr>
          <th class="fixed-side">Left Column</th>
          <td>Cell content</td>
          <td>Cell content longer</td>
          <td>Cell content</td>
          <td>Cell content</td>
          <td>Cell content</td>
          <td>Cell content</td>
          <td>Cell content</td>
        </tr>
      </tbody>
      <tfoot>
        <tr>
          <th class="fixed-side">&nbsp;</th>
          <td>Footer 2</td>
          <td>Footer 3</td>
          <td>Footer 4</td>
          <td>Footer 5</td>
          <td>Footer 6</td>
          <td>Footer 7</td>
          <td>Footer 8</td>
        </tr>
      </tfoot>
    </table>
  </div>
</div>

<p>See <a href="https://codepen.io/paulobrien/pen/LBrMxa" target="blank">position Sticky version </a>with no JS</p>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js" type="text/javascript"></script>
Schwann answered 23/7, 2019 at 15:12 Comment(0)
P
1

Opera was buggy for all of the previous answers when I tested them on my mac. If you scroll through the table the fixed column disappears after you pass the first unfixed column. I went ahead and wrote the code below. It works in all the browsers I have locally installed. I don't know how ie handles it though.

Just keep that in mind that if you intend to skip rows in one table and not the other or change the heights of the rows you might need to adjust this code.

<table class = "fixedColumns">
    <tr><td> row 1 </td></tr>
    <tr><td> row 2 </td></tr>
</table>
<table class = "scrollableTable">
    <tr><td> col 1 </td> <td> col 2 </td><td> col 3 </td><td> col 4 </td></tr>
    <tr><td> col 1 </td> <td> col 2 </td><td> col 3 </td><td> col 4 </td></tr>
</table>

<style type = "text/css" >
    .fixedColumns
    {
        vertical-align:top;
        display: inline-block;
    }
    .scrollableTable
    {
        display: inline-block;
        width:50px;
        white-space: nowrap;
        overflow-x: scroll;
    }
</style>
Prepare answered 19/6, 2013 at 22:10 Comment(0)
H
1
//If the table has tbody and thead, make them the relative container in which we can fix td and th as absolute

table tbody {
    position: relative;
}

table thead {
    position: relative;
}

//Make both the first header and first data cells (First column) absolute so that it sticks to the left

table td:first-of-type {
    position: absolute;
}

table th:first-of-type {
    position: absolute;
}

//Move Second column according to the width of column 1 

table td:nth-of-type(2) {
    padding-left: <Width of column 1>;
}

table th:nth-of-type(2) {
    padding-left: <Width of column 1>;
}
Hassle answered 19/3, 2014 at 14:5 Comment(1)
This worked really well for me and very simple to implement on existing tables by adding to current CSS - Thank you!Kilocalorie
C
1

I just made the right-most sticky column of a table sticky.

th:last-of-type {
 position: sticky;
 right: 0;
 width: 120px;
 background: #f7f7f7;
}


td:last-of-type {
 position: sticky;
 right: 0;
 background: #f7f7f7;
 width: 120px;
}

I believe if you'll do {position: sticky; left: 0;}, you'll get the desired result.

Caty answered 20/6, 2019 at 14:49 Comment(1)
Unfortunately sticky is not supported by IE.Tirpitz
D
1

<div style="max-width: 780px; overflow: scroll;">
    <table style="">
        <tr>
            <th style="position: sticky;left:0;background-color:aquamarine;">aaaaaaa</th>
            <th style="position: sticky;left:111px;background-color:aquamarine;">aaaaaaa</th>
            <th>aaaaaaa</th>
            <th>aaaaaaa</th>
            <th>aaaaaaa</th>
            <th>aaaaaaa</th>
            <th>aaaaaaa</th>
        </tr>
        <tr>
            <th style="position: sticky;left:0;background-color:aquamarine;">111111111111</th>
            <th style="position: sticky;left:111px;background-color:aquamarine;">111111111111</th>
            <th>111111111111</th>
            <th>111111111111</th>
            <th>111111111111</th>
            <th>111111111111</th>
            <th>111111111111</th>
        </tr>
    </table>
</div>
Daune answered 6/8, 2022 at 7:26 Comment(0)
C
1

Yet, another complete and working example of how to make a scrolling table with a frozen column on the right side.

This example reunites the CSS techniques mentioned in this post and some basic JavaScript functionality for the row buttons using only one event handler.

<!DOCTYPE html>
<html lang="en">

   <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>

      <style>
         /* Center the grid container */
         .container {
            width: 800px;
            height: 600px;
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
         }

         /* Grid style */
         #grid {
            margin-top: 25px;
            position: relative;
            left: 50%;
            transform: translateX(-50%);
            width: auto;
            max-width: 90%;
            min-width: 400px;
            overflow: hidden;
            height: auto;
            max-height: 350px;
         }

         /* Table border */
         table,
         th,
         td { border: 1px solid #c8c6c6; }

         /* Table size */
         table {
            width: 100%;
            height: 340px;
            margin: 0 auto;
            display: block;
            overflow-x: auto;
            border-spacing: 0;
         }

         tbody { white-space: nowrap; }

         caption {
            padding: 5px 10px;
            font-weight: bold;
            background: #929090;
            border-bottom: 1px solid #c8c6c6;
            position: sticky;
            top: 0;
            z-index: 2;
         }

         /* Columns style */
         th,
         td {
            padding: 5px 10px;
            border-top-width: 0;
            border-left-width: 0;
         }

         th {
            background: #929090;
            vertical-align: bottom;
            text-transform: capitalize;
         }

         th.fixed {
            position: sticky;
            right: 0;
            z-index: 2;
         }

         td.fixed {
            position: sticky;
            right: 0;
            z-index: 1;
            background: #fff;
         }

         th:last-child,
         td:last-child { border-right-width: 0; }

         tr:last-child td { border-bottom-width: 0; }

         /* Header & Footer row style */
         thead tr {
            position: sticky;
            top: 29px;
            z-index: 2;
         }

         tfoot tr {
            position: sticky;
            bottom: 0;
            z-index: 2;
         }

      </style>
   </head>

   <body>
      <div class="container">
         <div id="grid">
            <table>
               <caption>This is the caption</caption>
               <thead>
                  <tr>
                     <th></th>
                     <th></th>
                     <th></th>
                     <th></th>
                     <th></th>
                     <th></th>
                     <th></th>
                     <th class="fixed">Actions</th>
                  </tr>
               </thead>

               <tbody></tbody>

               <tfoot>
                  <tr>
                     <th>Total</th>
                     <th></th>
                     <th></th>
                     <th></th>
                     <th></th>
                     <th></th>
                     <th></th>
                     <th class="fixed"></th>
                  </tr>
               </tfoot>
            </table>
         </div>
      </div>

      <script>
         // Get some dummy data from the web.
         fetch('https://random-data-api.com/api/v2/beers?size=25')
         .then(res => res.json())
         .then(res => {
            let tHeaders = document.querySelectorAll('thead tr th')
            let keys = Object.keys(res[0]);

            // Manually pick some headers
            tHeaders[0].innerText = keys[3] // name
            tHeaders[1].innerText = keys[2] // brand
            tHeaders[2].innerText = keys[4] // style
            tHeaders[3].innerText = keys[5] // hop
            tHeaders[4].innerText = keys[6] // yeast
            tHeaders[5].innerText = keys[7] // malts
            tHeaders[6].innerText = keys[9] // alcohol

            // Add the dummy data to the table
            let tBody = document.querySelector('tbody')
            res.forEach(obj => {
               tBody.innerHTML += `<tr>
                                      <td>${obj.name}</td>
                                      <td>${obj.brand}</td>
                                      <td>${obj.style}</td>
                                      <td>${obj.hop}</td>
                                      <td>${obj.yeast}</td>
                                      <td>${obj.malts}</td>
                                      <td>${obj.alcohol}</td>
                                      <td class='fixed'><button class='editBtn'>Edit</button>&nbsp;<button class='delBtn'>Delete</button></td>
                                   </tr>`
            })

            // Add a click event handler to the grid.
            document.getElementById('grid').addEventListener('click', e => {
               let editBtn = e.target.closest('.editBtn')
               let delBtn = e.target.closest('.delBtn')
               let row = e.target.closest('tr')

               // Check if 'Edit' button was clicked...
               if (editBtn !== null) {
                  console.log(row.children[0].innerText + ' edition button clicked.')
               }
               // Check if 'Delete' was clicked...
               if (delBtn !== null) {
                  console.log(row.children[0].innerText + ' deletion button clicked')
               }
            })

         })
         
      </script>
   </body>

</html>
Capernaum answered 7/11, 2023 at 3:42 Comment(0)
C
1

* {
  box-sizing: border-box;
}

.content {
  --fixed-column-size: 50px;  
}

.inner-content {  
}

.table-component {
  overflow-x: visible;   
  position: relative; 
  border: 1px solid black;
}

.table-component.c1 .table-wrapper {
  overflow-x: scroll;  
  margin-left: var(--fixed-column-size);
}

.table-component.c2 .table-wrapper {
  overflow-x: scroll;  
  margin-left: calc(var(--fixed-column-size) * 2);
}

.table-component.c3 .table-wrapper {
  overflow-x: scroll;  
  margin-left: calc(var(--fixed-column-size) * 3);
}

table { 
  border-collapse: collapse;  
}

td {
  min-width: 100px;
}

th {
  min-width: 100px;  
}

.table-component.c1 th:nth-of-type(1),
.table-component.c1 td:nth-of-type(1),
.table-component.c2 th:nth-of-type(1),
.table-component.c2 td:nth-of-type(1),
.table-component.c3 th:nth-of-type(1),
.table-component.c3 td:nth-of-type(1)
{
  position: absolute;
  background: #aaa;  
  min-width: var(--fixed-column-size);
  left: 0;
}

.table-component.c2 th:nth-of-type(2),
.table-component.c2 td:nth-of-type(2),
.table-component.c3 th:nth-of-type(2),
.table-component.c3 td:nth-of-type(2)
{
  position: absolute;
  background: #aaa;  
  min-width: var(--fixed-column-size);
  left: var(--fixed-column-size);
}

.table-component.c3 th:nth-of-type(3),
.table-component.c3 td:nth-of-type(3)
{
  position: absolute;
  background: #aaa;  
  min-width: var(--fixed-column-size);
  left: calc(var(--fixed-column-size) * 2);
}

.space {
  height: 20px;
}
<div class="content">
  
    <p>1 column fixed</p>
  
    <div class="table-component c1">
    
      <div class="table-wrapper">

        <table>
          <thead>
            <tr>             
              <th>a1</th>
              <th>a2</th>
              <th>a3</th>
              <th>a4</th>
              <th>a5</th>
              <th>a6</th>
              <th>a7</th>
              <th>a8</th>  
              <th>a9</th>
              <th>a10</th>
              <th>a11</th>
              <th>a12</th>
              <th>a13</th>
              <th>a14</th>
              <th>a15</th>
              <th>a16</th>  
            </tr>
          </thead>
          <tbody>
            <tr>             
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
            </tr>
            <tr>             
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
            </tr>
          </tbody>
        </table>
      
      </div>

    </div>    
    
    <div class="space"></div>
    
    <p>2 columns fixed</p>
    
    <div class="table-component c2">
    
      <div class="table-wrapper">

        <table>
          <thead>
            <tr>             
              <th>a1</th>
              <th>a2</th>
              <th>a3</th>
              <th>a4</th>
              <th>a5</th>
              <th>a6</th>
              <th>a7</th>
              <th>a8</th>  
              <th>a9</th>
              <th>a10</th>
              <th>a11</th>
              <th>a12</th>
              <th>a13</th>
              <th>a14</th>
              <th>a15</th>
              <th>a16</th>  
            </tr>
          </thead>
          <tbody>
            <tr>             
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
            </tr>
            <tr>             
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
            </tr>
          </tbody>
        </table>
      
      </div>

    </div>
    
    <div class="space"></div>
    
    <p>3 columns fixed</p>
    
    <div class="table-component c3">
    
      <div class="table-wrapper">

        <table>
          <thead>
            <tr>              
              <th>a1</th>
              <th>a2</th>
              <th>a3</th>
              <th>a4</th>
              <th>a5</th>
              <th>a6</th>
              <th>a7</th>
              <th>a8</th>  
              <th>a9</th>
              <th>a10</th>
              <th>a11</th>
              <th>a12</th>
              <th>a13</th>
              <th>a14</th>
              <th>a15</th>
              <th>a16</th>  
            </tr>
          </thead>
          <tbody>
            <tr>             
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
            </tr>
            <tr>            
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
              <td>a</td>
            </tr>
          </tbody>
        </table>
      
      </div>

    </div>
    
</div>
Cheloid answered 4/1 at 19:15 Comment(0)
P
0

Alternatively, style the tbody with a predetermined size (via height:20em, for example) and use overflow-y:scroll;

Then, you can have a huge tbody, which will scroll independently of the rest of the page.

Parquet answered 21/8, 2009 at 14:33 Comment(1)
For the table body to be scrollable, it should display itself as a blockSha
M
0

$(document).ready(function() {
    var table = $('#example').DataTable( {
        scrollY:        "400px",
        scrollX:        true,
        scrollCollapse: true,
        paging:         true,
        fixedColumns:   {
            leftColumns: 3
        }
    } );
} );
<head>
	<title>table</title>
	
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdn.datatables.net/1.10.16/css/jquery.dataTables.min.css">
<link rel="stylesheet" href="https://cdn.datatables.net/fixedcolumns/3.2.4/css/fixedColumns.dataTables.min.css">
<script type="text/javascript" src="http://cdn.datatables.net/1.10.2/js/jquery.dataTables.min.js"></script>
<script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/1.10.16/js/jquery.dataTables.min.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/fixedcolumns/3.2.4/js/dataTables.fixedColumns.min.js"></script>


<style>
       th, td { white-space: nowrap; }
    div.dataTables_wrapper {
        width: 900px;
        margin: 0 auto;
    }
</style>

</head>
<table id="example" class="stripe row-border order-column" style="width:100%">
        <thead>
            <tr>
                <th>First name</th>
                <th>Last name</th>
                <th>Position</th>
                <th>Office</th>
                <th>Age</th>
                <th>Start date</th>
                <th>Salary</th>
                <th>Extn.</th>
                <th>E-mail</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>Tiger</td>
                <td>Nixon</td>
                <td>System Architect</td>
                <td>Edinburgh</td>
                <td>61</td>
                <td>2011/04/25</td>
                <td>$320,800</td>
                <td>5421</td>
                <td>[email protected]</td>
            </tr>
            <tr>
                <td>Garrett</td>
                <td>Winters</td>
                <td>Accountant</td>
                <td>Tokyo</td>
                <td>63</td>
                <td>2011/07/25</td>
                <td>$170,750</td>
                <td>8422</td>
                <td>[email protected]</td>
            </tr>
            <tr>
                <td>Ashton</td>
                <td>Cox</td>
                <td>Junior Technical Author</td>
                <td>San Francisco</td>
                <td>66</td>
                <td>2009/01/12</td>
                <td>$86,000</td>
                <td>1562</td>
                <td>[email protected]</td>
            </tr>
            <tr>
                <td>Cedric</td>
                <td>Kelly</td>
                <td>Senior Javascript Developer</td>
                <td>Edinburgh</td>
                <td>22</td>
                <td>2012/03/29</td>
                <td>$433,060</td>
                <td>6224</td>
                <td>[email protected]</td>
            </tr>
            <tr>
                <td>Airi</td>
                <td>Satou</td>
                <td>Accountant</td>
                <td>Tokyo</td>
                <td>33</td>
                <td>2008/11/28</td>
                <td>$162,700</td>
                <td>5407</td>
                <td>[email protected]</td>
            </tr>
            <tr>
                <td>Brielle</td>
                <td>Williamson</td>
                <td>Integration Specialist</td>
                <td>New York</td>
                <td>61</td>
                <td>2012/12/02</td>
                <td>$372,000</td>
                <td>4804</td>
                <td>[email protected]</td>
            </tr>
            <tr>
                <td>Herrod</td>
                <td>Chandler</td>
                <td>Sales Assistant</td>
                <td>San Francisco</td>
                <td>59</td>
                <td>2012/08/06</td>
                <td>$137,500</td>
                <td>9608</td>
                <td>[email protected]</td>
            </tr>
            <tr>
                <td>Rhona</td>
                <td>Davidson</td>
                <td>Integration Specialist</td>
                <td>Tokyo</td>
                <td>55</td>
                <td>2010/10/14</td>
                <td>$327,900</td>
                <td>6200</td>
                <td>[email protected]</td>
            </tr>
            <tr>
                <td>Colleen</td>
                <td>Hurst</td>
                <td>Javascript Developer</td>
                <td>San Francisco</td>
                <td>39</td>
                <td>2009/09/15</td>
                <td>$205,500</td>
                <td>2360</td>
                <td>[email protected]</td>
            </tr>
            <tr>
                <td>Sonya</td>
                <td>Frost</td>
                <td>Software Engineer</td>
                <td>Edinburgh</td>
                <td>23</td>
                <td>2008/12/13</td>
                <td>$103,600</td>
                <td>1667</td>
                <td>[email protected]</td>
            </tr>
            <tr>
                <td>Jena</td>
                <td>Gaines</td>
                <td>Office Manager</td>
                <td>London</td>
                <td>30</td>
                <td>2008/12/19</td>
                <td>$90,560</td>
                <td>3814</td>
                <td>[email protected]</td>
            </tr>
             <tr>
                <td>Sakura</td>
                <td>Yamamoto</td>
                <td>Support Engineer</td>
                <td>Tokyo</td>
                <td>37</td>
                <td>2009/08/19</td>
                <td>$139,575</td>
                <td>9383</td>
                <td>[email protected]</td>
            </tr>
            <tr>
                <td>Thor</td>
                <td>Walton</td>
                <td>Developer</td>
                <td>New York</td>
                <td>61</td>
                <td>2013/08/11</td>
                <td>$98,540</td>
                <td>8327</td>
                <td>[email protected]</td>
            </tr>
            <tr>
                <td>Finn</td>
                <td>Camacho</td>
                <td>Support Engineer</td>
                <td>San Francisco</td>
                <td>47</td>
                <td>2009/07/07</td>
                <td>$87,500</td>
                <td>2927</td>
                <td>[email protected]</td>
            </tr>
            <tr>
                <td>Serge</td>
                <td>Baldwin</td>
                <td>Data Coordinator</td>
                <td>Singapore</td>
                <td>64</td>
                <td>2012/04/09</td>
                <td>$138,575</td>
                <td>8352</td>
                <td>[email protected]</td>
            </tr>
            <tr>
                <td>Zenaida</td>
                <td>Frank</td>
                <td>Software Engineer</td>
                <td>New York</td>
                <td>63</td>
                <td>2010/01/04</td>
                <td>$125,250</td>
                <td>7439</td>
                <td>[email protected]</td>
            </tr>
            <tr>
                <td>Zorita</td>
                <td>Serrano</td>
                <td>Software Engineer</td>
                <td>San Francisco</td>
                <td>56</td>
                <td>2012/06/01</td>
                <td>$115,000</td>
                <td>4389</td>
                <td>[email protected]</td>
            </tr>
            <tr>
                <td>Jennifer</td>
                <td>Acosta</td>
                <td>Junior Javascript Developer</td>
                <td>Edinburgh</td>
                <td>43</td>
                <td>2013/02/01</td>
                <td>$75,650</td>
                <td>3431</td>
                <td>[email protected]</td>
            </tr>
            <tr>
                <td>Cara</td>
                <td>Stevens</td>
                <td>Sales Assistant</td>
                <td>New York</td>
                <td>46</td>
                <td>2011/12/06</td>
                <td>$145,600</td>
                <td>3990</td>
                <td>[email protected]</td>
            </tr>
            <tr>
                <td>Hermione</td>
                <td>Butler</td>
                <td>Regional Director</td>
                <td>London</td>
                <td>47</td>
                <td>2011/03/21</td>
                <td>$356,250</td>
                <td>1016</td>
                <td>[email protected]</td>
            </tr>
            <tr>
                <td>Lael</td>
                <td>Greer</td>
                <td>Systems Administrator</td>
                <td>London</td>
                <td>21</td>
                <td>2009/02/27</td>
                <td>$103,500</td>
                <td>6733</td>
                <td>[email protected]</td>
            </tr>
            <tr>
                <td>Jonas</td>
                <td>Alexander</td>
                <td>Developer</td>
                <td>San Francisco</td>
                <td>30</td>
                <td>2010/07/14</td>
                <td>$86,500</td>
                <td>8196</td>
                <td>[email protected]</td>
            </tr>
            <tr>
                <td>Shad</td>
                <td>Decker</td>
                <td>Regional Director</td>
                <td>Edinburgh</td>
                <td>51</td>
                <td>2008/11/13</td>
                <td>$183,000</td>
                <td>6373</td>
                <td>[email protected]</td>
            </tr>
            <tr>
                <td>Michael</td>
                <td>Bruce</td>
                <td>Javascript Developer</td>
                <td>Singapore</td>
                <td>29</td>
                <td>2011/06/27</td>
                <td>$183,000</td>
                <td>5384</td>
                <td>[email protected]</td>
            </tr>
            <tr>
                <td>Donna</td>
                <td>Snider</td>
                <td>Customer Support</td>
                <td>New York</td>
                <td>27</td>
                <td>2011/01/25</td>
                <td>$112,000</td>
                <td>4226</td>
                <td>[email protected]</td>
            </tr>
        </tbody>
    </table>

This can be easily done with the help of datatables. People who are new to data tables, please refer to https://datatables.net/ .Its a plugin and offers a lot of features.In the the code given, header is fixed,first 3 columns are fixed and several other features are also there.

Muffler answered 25/4, 2018 at 12:6 Comment(1)
doesnt work if you need both a fixed column and a fixed header at the same timeSaul
G
0

I didn't check each and every answer for this question, but after analyzing most of them I found that design fails in case of multiline data in cells or head. I used Javascript to solve this. I hope someone finds this helpful.

https://codepen.io/kushagrarora/pen/zeYaoY

var freezeTables = document.getElementsByClassName("freeze-pane");

[].forEach.call(freezeTables, ftable => {
  var wrapper = document.createElement("div");
  wrapper.className = "freeze-pane-wrapper";
  var scroll = document.createElement("div");
  scroll.className = "freeze-pane-scroll";

  wrapper.appendChild(scroll);

  ftable.parentNode.replaceChild(wrapper, ftable);

  scroll.appendChild(ftable);

  var heads = ftable.querySelectorAll("th:first-child");

  let maxWidth = 0;

  [].forEach.call(heads, head => {
    var w = window
      .getComputedStyle(head)
      .getPropertyValue("width")
      .split("px")[0];
    if (Number(w) > Number(maxWidth)) maxWidth = w;
  });

  ftable.parentElement.style.marginLeft = maxWidth + "px";
  ftable.parentElement.style.width = "calc(100% - " + maxWidth + "px)";
  [].forEach.call(heads, head => {
    head.style.width = maxWidth + "px";
    var restRowHeight = window
      .getComputedStyle(head.nextElementSibling)
      .getPropertyValue("height");
    var headHeight = window.getComputedStyle(head).getPropertyValue("height");
    if (headHeight > restRowHeight)
      head.nextElementSibling.style.height = headHeight;
    else head.style.height = restRowHeight;
  });
});
@import url("https://fonts.googleapis.com/css?family=Open+Sans");
* {
  font-family: "Open Sans", sans-serif;
}

.container {
  width: 400px;
  height: 90vh;
  border: 1px solid black;
  overflow: hidden;
}

table,
th,
td {
  border: 1px solid #eee;
}

.table {
  width: 100%;
  margin-bottom: 1rem;
  table-layout: fixed;
  border-collapse: collapse;
}

.freeze-pane-wrapper {
  position: relative;
}

.freeze-pane-scroll {
  overflow-x: scroll;
  overflow-y: visible;
}

.freeze-pane th:first-child {
  position: absolute;
  background-color: pink;
  left: 0;
  top: auto;
  max-width: 40%;
}
<div class="container">
  <table class="freeze-pane">
    <tbody>
      <tr>
        <th>
          <p>Model</p>
        </th>
        <th>
          <p>Mercedes Benz AMG C43 4dr</p>
        </th>
        <th>
          <p>Audi S4 Premium 4dr</p>
        </th>
        <th>
          <p>BMW 440i 4dr sedan</p>
        </th>
      </tr>
      <tr>
        <th>
          <p>Passenger capacity</p>
        </th>
        <td>
          <p>5</p>
        </td>
        <td>
          <p>5</p>
        </td>
        <td>
          <p>5</p>
        </td>
      </tr>
      <tr>
        <th>
          <p>Front (Head/Shoulder/Leg) (In.)</p>
        </th>
        <td>
          <p>37.1/55.3/41.7</p>
        </td>
        <td>
          <p>38.9/55.9/41.3</p>
        </td>
        <td>
          <p>39.9/54.8/42.2</p>
        </td>
      </tr>
      <tr>
        <th>
          <p>Second (Head/Shoulder/Leg) (In.)</p>
        </th>
        <td>
          <p>37.1/55.5/35.2</p>
        </td>
        <td>
          <p>37.4/54.5/35.7</p>
        </td>
        <td>
          <p>36.9/54.3/33.7</p>
        </td>
      </tr>
    </tbody>
  </table>
</div>

Note: the "container" div is just to demonstrate that code is compatible with mobile-view.

Ghibelline answered 23/1, 2019 at 8:4 Comment(0)
S
0

I improve Circuit Breaker's example, original example code will shake fixed columns since table has padding, I use border-collapse: collapse disable it

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <!--mobile friendly-->
  <meta name="viewport" content="width=device-width, user-scalable=yes">
  <style>
    .view {
      margin: auto;
      width: 600px;
    }

    .wrapper {
      position: relative;
      overflow: auto;
      border: 1px solid black;
      white-space: nowrap;
    }

    .sticky-col {
      position: -webkit-sticky;
      position: sticky;
      background-color: white;
    }

    .first-col {
      padding: 0;
      margin: 0;
      left: 0;
    }

    .second-col {
      left: 200px;
    }

    .col {
      width: 200px;
      min-width: 200px;
      max-width: 200px;
    }
  </style>
</head>
<body>
<div class="view">
  <div class="wrapper">
    <table class="table" style="border-collapse: collapse;">
      <colgroup>
        <col class="col"/>
        <col class="col"/>
        <col class="col"/>
        <col class="col"/>
      </colgroup>
      <thead>
      <tr>
        <th class="sticky-col first-col">Number</th>
        <th class="sticky-col second-col">First Name</th>
        <th>Last Name</th>
        <th>Employer</th>
      </tr>
      </thead>
      <tbody>
      <tr>
        <td class="sticky-col first-col">1</td>
        <td class="sticky-col second-col">Mark</td>
        <td>Ham</td>
        <td>Micro</td>
      </tr>
      <tr>
        <td class="sticky-col first-col">2</td>
        <td class="sticky-col second-col">Jacob</td>
        <td>Smith</td>
        <td>Adob Adob Adob AdobAdob Adob Adob Adob Adob</td>
      </tr>
      <tr>
        <td class="sticky-col first-col">3</td>
        <td class="sticky-col second-col">Larry</td>
        <td>Wen</td>
        <td>Goog Goog Goog GoogGoog Goog Goog Goog Goog Goog</td>
      </tr>
      </tbody>
    </table>
  </div>
</div>
</body>
</html>
Sheets answered 17/1, 2023 at 3:52 Comment(0)
U
0

This seems to work in chrome

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Sticky First Column in CSS</title>
  <style>
    table {
      width: 100%;
      border-collapse: collapse;
    }
    
    th,
    td {
      padding: 8px;
      border: 1px solid #dddddd;
      text-align: left;
    }
    
    td:nth-of-type(1),
    th:nth-of-type(1) {
      position: sticky;
      background: lightgoldenrodyellow;
      left: 0;
    }
  </style>
</head>

<body>

  <table>
    <thead>
      <tr>
        <th>Column 1</th>
        <th>Column 2</th>
        <th>Column 3</th>
        <th>Column 4</th>
        <th>Column 5</th>
        <th>Column 6</th>
        <th>Column 7</th>
        <th>Column 8</th>
        <th>Column 9</th>
        <!-- Add more columns if needed -->
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Data 1-1</td>
        <td>Data 1-2</td>
        <td>Data 1-3</td>
        <td>Data 1-4</td>
        <td>Data 1-5</td>
        <td>Data 1-6</td>
        <td>Data 1-7</td>
        <td>Data 1-8</td>
        <td>Data 1-9</td>
      </tr>
      <tr>
        <td>Data 2-1</td>
        <td>Data 2-2</td>
        <td>Data 2-3</td>
        <td>Data 2-4</td>
        <td>Data 2-5</td>
        <td>Data 2-6</td>
        <td>Data 2-7</td>
        <td>Data 2-8</td>
        <td>Data 2-9</td>
      </tr>
      <!-- Add more rows if needed -->
    </tbody>
  </table>

</body>

</html>
Uninterrupted answered 17/3 at 10:9 Comment(0)
M
-1

In HTML5, you can use CSS style.transform.
However, i reccomend you "swipe between pages" turn off If you implement on Mac.

look at sample codePen

let l  = 0;
let t  = 0;

const MouseWheelHandler = (e) => {
  // vertical scroll
  if (e.deltaX == -0) {
    // t = t - e.deltaY

  // horizonal scroll
  } else if (e.deltaY == -0) {
    l = l - e.deltaX
    if (l >= 0) {
      l = 0;
      document.getElementById("gantt_task").style.transform = "translateX(1px)"
      document.getElementById("gantt_task_header").style.transform = "translateX(1px)"
      return false
    } 
    document.getElementById("gantt_task").style.transform = "translateX(" + l.toString() + "px)"
    document.getElementById("gantt_task_header").style.transform = "translateX(" + l.toString() + "px)"
  }
  return false;
}

window.addEventListener("wheel", MouseWheelHandler, false);
.row {
  border-bottom: 1px solid #979A9A
}
#gantt_grid_header {
  height:   30px;
  width:    100px;
  position: fixed;
  z-index:  3;
  top:      0px;
  left:     0px;
  border:   1px solid #cecece;
  background-color: #F08080;
}     

#gantt_task_header {
  height:   30px;
  width:    400px;
  position: fixed;
  z-index:  2;
  top:      0px;
  left:     100px;
  border:   1px solid #cecece;
  background-color: #FFC300;
}

#gantt_grid {
  width:    100px; 
  height:   400px;
  position: absolute;
  left:     0px;
  top:      0px;
  z-index:  1;
  border:   1px solid #cecece;
  background-color: #DAF7A6;
}

#gantt_task {
  width:    400px; 
  height:   400px;
  position: absolute;
  left:     100px;
  top:      0px;
  border:   1px solid #cecece;
  background-color: #FF5733;
}
<html>
    <div id="gantt_grid_header">
      HEADER
    </div>
    <div id="gantt_grid">
      <div class="row">V Scroll OK</div>
      <div class="row">V Scroll OK</div>
      <div class="row">V Scroll OK</div>
      <div class="row">V Scroll OK</div>
      <div class="row">V Scroll OK</div>
      <div class="row">V Scroll OK</div>
      <div class="row">V Scroll OK</div>
      <div class="row">V Scroll OK</div>
      <div class="row">V Scroll OK</div>
    </div>
    <div id="gantt_task_header">
      DATA HEADER
    </div>
    <div id="gantt_task">
      <div class="row">Vertical,Horizenal Scroll OK</div>
      <div class="row">Vertical,Horizenal Scroll OK</div>
      <div class="row">Vertical,Horizenal Scroll OK</div>
      <div class="row">Vertical,Horizenal Scroll OK</div>
      <div class="row">Vertical,Horizenal Scroll OK</div>
      <div class="row">Vertical,Horizenal Scroll OK</div>
      <div class="row">Vertical,Horizenal Scroll OK</div>
      <div class="row">Vertical,Horizenal Scroll OK</div>
      <div class="row">Vertical,Horizenal Scroll OK</div>
    </div>
</html>
Mythopoeia answered 22/6, 2017 at 12:32 Comment(1)
It'd be a rare situation where you could expect your users to turn off an OS feature to use your website.Christoffer
W
-1

If you don't want to touch your current table too much you can make a fake pinned column in front of the table.

The example shows one way of doing it without JS

table {
  border-collapse: collapse;
  border-spacing: 0;
  border: 1px solid #ddd;
  min-width: 600px;
}

.labels {
  display:flex;
  flex-direction: column
}

.overflow {
  overflow-x: scroll;
  min width: 400px;
  flex: 1;
}

.label {
  display: flex;
  align-items: center;
  white-space:nowrap;
  padding: 10px;
  flex: 1;
  border-bottom: 1px solid #ddd;
  border-right: 2px solid #ddd;
}

.label:last-of-type {
  overflow-x: scroll;
  border-bottom: 0;
}

td {
  border: 1px solid #ddd;
  padding: 10px;
}

.flex {
  display:flex;
  max-width: 600px;
  padding: 0;
  border: 5px solid #ddd;
}
<div class="flex">
  <div class="labels">
    <span class="label">Label 1</span>
    <span class="label">Lorem ipsum dolor sit amet.</span>
    <span class="label">Lorem ipsum dolor.</span>
  </div>
  <div class="overflow">
    <table>
      <tr>
        <td class="long">Lorem ipsum dolor sit amet consectetur adipisicing</td>
        <td class="long">Lorem ipsum dolor sit amet consectetur adipisicing</td>
      </tr>
      <tr>
        <td class="long">Lorem ipsum dolor sit amet consectetur adipisicing</td>
        <td class="long">Lorem ipsum dolor sit amet consectetur adipisicing</td>
      </tr>
      <tr>
        <td class="long">Lorem ipsum dolor sit amet consectetur adipisicing</td>
        <td class="long">Lorem ipsum dolor sit amet consectetur adipisicing</td>
      </tr>
  </table>
  </div>
</div>
Warp answered 26/6, 2019 at 13:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.