[html] How do I create an HTML table with a fixed/frozen left column and a scrollable body?

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.

This question is related to html css html-table

The answer is


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;

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.


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.

_x000D_
_x000D_
.wrapper {_x000D_
    overflow-x: scroll;_x000D_
}_x000D_
_x000D_
td {_x000D_
    min-width: 50px;_x000D_
}_x000D_
_x000D_
.fixed {_x000D_
    position: absolute;_x000D_
    background: #aaa;_x000D_
}
_x000D_
<div class="content" style="width: 400px">_x000D_
_x000D_
  <div class="wrapper" style="margin-left: 100px">_x000D_
_x000D_
      <table>_x000D_
        <thead>_x000D_
          <tr>_x000D_
            <th class="fixed" style="left: 0px">aaa</th>_x000D_
            <th class="fixed" style="left: 50px">aaa2</th>_x000D_
            <th>a</th>_x000D_
            <th>b</th>_x000D_
            <th>c</th>_x000D_
            <th>d</th>_x000D_
            <th>e</th>_x000D_
            <th>f</th>_x000D_
            <th>a</th>_x000D_
            <th>b</th>_x000D_
            <th>c</th>_x000D_
            <th>d</th>_x000D_
            <th>e</th>_x000D_
            <th>f</th>_x000D_
            <th>a</th>_x000D_
            <th>b</th>_x000D_
            <th>c</th>_x000D_
            <th>d</th>_x000D_
            <th>e</th>_x000D_
            <th>f</th>_x000D_
            <th>a</th>_x000D_
            <th>b</th>_x000D_
            <th>c</th>_x000D_
            <th>d</th>_x000D_
            <th>e</th>_x000D_
            <th>f</th>        _x000D_
          </tr>_x000D_
        </thead>_x000D_
        <tbody>_x000D_
          <tr>_x000D_
            <td class="fixed" style="left: 0px">aaa</td>_x000D_
            <td class="fixed" style="left: 50px">aaa2</td>_x000D_
            <td>a</td>_x000D_
            <td>b</td>_x000D_
            <td>c</td>_x000D_
            <td>d</td>_x000D_
            <td>e</td>_x000D_
            <td>f</td>_x000D_
            <td>a</td>_x000D_
            <td>b</td>_x000D_
            <td>c</td>_x000D_
            <td>d</td>_x000D_
            <td>e</td>_x000D_
            <td>f</td>_x000D_
            <td>a</td>_x000D_
            <td>b</td>_x000D_
            <td>c</td>_x000D_
            <td>d</td>_x000D_
            <td>e</td>_x000D_
            <td>f</td>_x000D_
            <td>a</td>_x000D_
            <td>b</td>_x000D_
            <td>c</td>_x000D_
            <td>d</td>_x000D_
            <td>e</td>_x000D_
            <td>f</td>_x000D_
          </tr>_x000D_
          <tr>_x000D_
            <td class="fixed" style="left: 0">bbb</td>_x000D_
            <td class="fixed" style="left: 50px">bbb2</td>_x000D_
            <td>a</td>_x000D_
            <td>b</td>_x000D_
            <td>c</td>_x000D_
            <td>d</td>_x000D_
            <td>e</td>_x000D_
            <td>f</td>_x000D_
            <td>a</td>_x000D_
            <td>b</td>_x000D_
            <td>c</td>_x000D_
            <td>d</td>_x000D_
            <td>e</td>_x000D_
            <td>f</td>_x000D_
            <td>a</td>_x000D_
            <td>b</td>_x000D_
            <td>c</td>_x000D_
            <td>d</td>_x000D_
            <td>e</td>_x000D_
            <td>f</td>_x000D_
            <td>a</td>_x000D_
            <td>b</td>_x000D_
            <td>c</td>_x000D_
            <td>d</td>_x000D_
            <td>e</td>_x000D_
            <td>f</td>_x000D_
          </tr>_x000D_
        </tbody>_x000D_
      </table>_x000D_
_x000D_
  </div>_x000D_
_x000D_
</div>
_x000D_
_x000D_
_x000D_


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

_x000D_
_x000D_
var freezeTables = document.getElementsByClassName("freeze-pane");_x000D_
_x000D_
[].forEach.call(freezeTables, ftable => {_x000D_
  var wrapper = document.createElement("div");_x000D_
  wrapper.className = "freeze-pane-wrapper";_x000D_
  var scroll = document.createElement("div");_x000D_
  scroll.className = "freeze-pane-scroll";_x000D_
_x000D_
  wrapper.appendChild(scroll);_x000D_
_x000D_
  ftable.parentNode.replaceChild(wrapper, ftable);_x000D_
_x000D_
  scroll.appendChild(ftable);_x000D_
_x000D_
  var heads = ftable.querySelectorAll("th:first-child");_x000D_
_x000D_
  let maxWidth = 0;_x000D_
_x000D_
  [].forEach.call(heads, head => {_x000D_
    var w = window_x000D_
      .getComputedStyle(head)_x000D_
      .getPropertyValue("width")_x000D_
      .split("px")[0];_x000D_
    if (Number(w) > Number(maxWidth)) maxWidth = w;_x000D_
  });_x000D_
_x000D_
  ftable.parentElement.style.marginLeft = maxWidth + "px";_x000D_
  ftable.parentElement.style.width = "calc(100% - " + maxWidth + "px)";_x000D_
  [].forEach.call(heads, head => {_x000D_
    head.style.width = maxWidth + "px";_x000D_
    var restRowHeight = window_x000D_
      .getComputedStyle(head.nextElementSibling)_x000D_
      .getPropertyValue("height");_x000D_
    var headHeight = window.getComputedStyle(head).getPropertyValue("height");_x000D_
    if (headHeight > restRowHeight)_x000D_
      head.nextElementSibling.style.height = headHeight;_x000D_
    else head.style.height = restRowHeight;_x000D_
  });_x000D_
});
_x000D_
@import url("https://fonts.googleapis.com/css?family=Open+Sans");_x000D_
* {_x000D_
  font-family: "Open Sans", sans-serif;_x000D_
}_x000D_
_x000D_
.container {_x000D_
  width: 400px;_x000D_
  height: 90vh;_x000D_
  border: 1px solid black;_x000D_
  overflow: hidden;_x000D_
}_x000D_
_x000D_
table,_x000D_
th,_x000D_
td {_x000D_
  border: 1px solid #eee;_x000D_
}_x000D_
_x000D_
.table {_x000D_
  width: 100%;_x000D_
  margin-bottom: 1rem;_x000D_
  table-layout: fixed;_x000D_
  border-collapse: collapse;_x000D_
}_x000D_
_x000D_
.freeze-pane-wrapper {_x000D_
  position: relative;_x000D_
}_x000D_
_x000D_
.freeze-pane-scroll {_x000D_
  overflow-x: scroll;_x000D_
  overflow-y: visible;_x000D_
}_x000D_
_x000D_
.freeze-pane th:first-child {_x000D_
  position: absolute;_x000D_
  background-color: pink;_x000D_
  left: 0;_x000D_
  top: auto;_x000D_
  max-width: 40%;_x000D_
}
_x000D_
<div class="container">_x000D_
  <table class="freeze-pane">_x000D_
    <tbody>_x000D_
      <tr>_x000D_
        <th>_x000D_
          <p>Model</p>_x000D_
        </th>_x000D_
        <th>_x000D_
          <p>Mercedes Benz AMG C43 4dr</p>_x000D_
        </th>_x000D_
        <th>_x000D_
          <p>Audi S4 Premium 4dr</p>_x000D_
        </th>_x000D_
        <th>_x000D_
          <p>BMW 440i 4dr sedan</p>_x000D_
        </th>_x000D_
      </tr>_x000D_
      <tr>_x000D_
        <th>_x000D_
          <p>Passenger capacity</p>_x000D_
        </th>_x000D_
        <td>_x000D_
          <p>5</p>_x000D_
        </td>_x000D_
        <td>_x000D_
          <p>5</p>_x000D_
        </td>_x000D_
        <td>_x000D_
          <p>5</p>_x000D_
        </td>_x000D_
      </tr>_x000D_
      <tr>_x000D_
        <th>_x000D_
          <p>Front (Head/Shoulder/Leg) (In.)</p>_x000D_
        </th>_x000D_
        <td>_x000D_
          <p>37.1/55.3/41.7</p>_x000D_
        </td>_x000D_
        <td>_x000D_
          <p>38.9/55.9/41.3</p>_x000D_
        </td>_x000D_
        <td>_x000D_
          <p>39.9/54.8/42.2</p>_x000D_
        </td>_x000D_
      </tr>_x000D_
      <tr>_x000D_
        <th>_x000D_
          <p>Second (Head/Shoulder/Leg) (In.)</p>_x000D_
        </th>_x000D_
        <td>_x000D_
          <p>37.1/55.5/35.2</p>_x000D_
        </td>_x000D_
        <td>_x000D_
          <p>37.4/54.5/35.7</p>_x000D_
        </td>_x000D_
        <td>_x000D_
          <p>36.9/54.3/33.7</p>_x000D_
        </td>_x000D_
      </tr>_x000D_
    </tbody>_x000D_
  </table>_x000D_
</div>
_x000D_
_x000D_
_x000D_

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


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.


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.

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

Hope this helps someone!


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

_x000D_
_x000D_
table {_x000D_
  border-collapse: collapse;_x000D_
  border-spacing: 0;_x000D_
  border: 1px solid #ddd;_x000D_
  min-width: 600px;_x000D_
}_x000D_
_x000D_
.labels {_x000D_
  display:flex;_x000D_
  flex-direction: column_x000D_
}_x000D_
_x000D_
.overflow {_x000D_
  overflow-x: scroll;_x000D_
  min width: 400px;_x000D_
  flex: 1;_x000D_
}_x000D_
_x000D_
.label {_x000D_
  display: flex;_x000D_
  align-items: center;_x000D_
  white-space:nowrap;_x000D_
  padding: 10px;_x000D_
  flex: 1;_x000D_
  border-bottom: 1px solid #ddd;_x000D_
  border-right: 2px solid #ddd;_x000D_
}_x000D_
_x000D_
.label:last-of-type {_x000D_
  overflow-x: scroll;_x000D_
  border-bottom: 0;_x000D_
}_x000D_
_x000D_
td {_x000D_
  border: 1px solid #ddd;_x000D_
  padding: 10px;_x000D_
}_x000D_
_x000D_
.flex {_x000D_
  display:flex;_x000D_
  max-width: 600px;_x000D_
  padding: 0;_x000D_
  border: 5px solid #ddd;_x000D_
}
_x000D_
<div class="flex">_x000D_
  <div class="labels">_x000D_
    <span class="label">Label 1</span>_x000D_
    <span class="label">Lorem ipsum dolor sit amet.</span>_x000D_
    <span class="label">Lorem ipsum dolor.</span>_x000D_
  </div>_x000D_
  <div class="overflow">_x000D_
    <table>_x000D_
      <tr>_x000D_
        <td class="long">Lorem ipsum dolor sit amet consectetur adipisicing</td>_x000D_
        <td class="long">Lorem ipsum dolor sit amet consectetur adipisicing</td>_x000D_
      </tr>_x000D_
      <tr>_x000D_
        <td class="long">Lorem ipsum dolor sit amet consectetur adipisicing</td>_x000D_
        <td class="long">Lorem ipsum dolor sit amet consectetur adipisicing</td>_x000D_
      </tr>_x000D_
      <tr>_x000D_
        <td class="long">Lorem ipsum dolor sit amet consectetur adipisicing</td>_x000D_
        <td class="long">Lorem ipsum dolor sit amet consectetur adipisicing</td>_x000D_
      </tr>_x000D_
  </table>_x000D_
  </div>_x000D_
</div>
_x000D_
_x000D_
_x000D_


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>

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


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.


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.

_x000D_
_x000D_
.tscroll {_x000D_
  width: 400px;_x000D_
  overflow-x: scroll;_x000D_
  margin-bottom: 10px;_x000D_
  border: solid black 1px;_x000D_
}_x000D_
_x000D_
.tscroll table td:first-child {_x000D_
  position: sticky;_x000D_
  left: 0;_x000D_
  background-color: #ddd;_x000D_
}_x000D_
_x000D_
.tscroll td, .tscroll th {_x000D_
  border-bottom: dashed #888 1px;_x000D_
}
_x000D_
<html>_x000D_
<div class="tscroll">_x000D_
  <table>_x000D_
    <thead>_x000D_
      <tr>_x000D_
        <th></th>_x000D_
        <th colspan="5">Heading 1</th>_x000D_
        <th colspan="8">Heading 2</th>_x000D_
        <th colspan="4">Heading 3</th>_x000D_
      </tr>_x000D_
    </thead>_x000D_
    <tbody>_x000D_
      <tr>_x000D_
        <td>9:00</td>_x000D_
        <td>AAA</td>_x000D_
        <td>BBB</td>_x000D_
        <td>CCC</td>_x000D_
        <td>DDD</td>_x000D_
        <td>EEE</td>_x000D_
        <td>FFF</td>_x000D_
        <td>GGG</td>_x000D_
        <td>HHH</td>_x000D_
        <td>III</td>_x000D_
        <td>JJJ</td>_x000D_
        <td>KKK</td>_x000D_
        <td>LLL</td>_x000D_
        <td>MMM</td>_x000D_
        <td>NNN</td>_x000D_
        <td>OOO</td>_x000D_
        <td>PPP</td>_x000D_
        <td>QQQ</td>_x000D_
      </tr>_x000D_
      <tr>_x000D_
        <td>10:00</td>_x000D_
        <td>AAA</td>_x000D_
        <td>BBB</td>_x000D_
        <td>CCC</td>_x000D_
        <td>DDD</td>_x000D_
        <td>EEE</td>_x000D_
        <td>FFF</td>_x000D_
        <td>GGG</td>_x000D_
        <td>HHH</td>_x000D_
        <td>III</td>_x000D_
        <td>JJJ</td>_x000D_
        <td>KKK</td>_x000D_
        <td>LLL</td>_x000D_
        <td>MMM</td>_x000D_
        <td>NNN</td>_x000D_
        <td>OOO</td>_x000D_
        <td>PPP</td>_x000D_
        <td>QQQ</td>_x000D_
      </tr>_x000D_
      <tr>_x000D_
        <td>11:00</td>_x000D_
        <td>AAA</td>_x000D_
        <td>BBB</td>_x000D_
        <td>CCC</td>_x000D_
        <td>DDD</td>_x000D_
        <td>EEE</td>_x000D_
        <td>FFF</td>_x000D_
        <td>GGG</td>_x000D_
        <td>HHH</td>_x000D_
        <td>III</td>_x000D_
        <td>JJJ</td>_x000D_
        <td>KKK</td>_x000D_
        <td>LLL</td>_x000D_
        <td>MMM</td>_x000D_
        <td>NNN</td>_x000D_
        <td>OOO</td>_x000D_
        <td>PPP</td>_x000D_
        <td>QQQ</td>_x000D_
      </tr>_x000D_
      <tr>_x000D_
        <td>12:00</td>_x000D_
        <td>AAA</td>_x000D_
        <td>BBB</td>_x000D_
        <td>CCC</td>_x000D_
        <td>DDD</td>_x000D_
        <td>EEE</td>_x000D_
        <td>FFF</td>_x000D_
        <td>GGG</td>_x000D_
        <td>HHH</td>_x000D_
        <td>III</td>_x000D_
        <td>JJJ</td>_x000D_
        <td>KKK</td>_x000D_
        <td>LLL</td>_x000D_
        <td>MMM</td>_x000D_
        <td>NNN</td>_x000D_
        <td>OOO</td>_x000D_
        <td>PPP</td>_x000D_
        <td>QQQ</td>_x000D_
      </tr>_x000D_
      <tr>_x000D_
        <td>13:00</td>_x000D_
        <td>AAA</td>_x000D_
        <td>BBB</td>_x000D_
        <td>CCC</td>_x000D_
        <td>DDD</td>_x000D_
        <td>EEE</td>_x000D_
        <td>FFF</td>_x000D_
        <td>GGG</td>_x000D_
        <td>HHH</td>_x000D_
        <td>III</td>_x000D_
        <td>JJJ</td>_x000D_
        <td>KKK</td>_x000D_
        <td>LLL</td>_x000D_
        <td>MMM</td>_x000D_
        <td>NNN</td>_x000D_
        <td>OOO</td>_x000D_
        <td>PPP</td>_x000D_
        <td>QQQ</td>_x000D_
      </tr>_x000D_
      <tr>_x000D_
        <td>14:00</td>_x000D_
        <td>AAA</td>_x000D_
        <td>BBB</td>_x000D_
        <td>CCC</td>_x000D_
        <td>DDD</td>_x000D_
        <td>EEE</td>_x000D_
        <td>FFF</td>_x000D_
        <td>GGG</td>_x000D_
        <td>HHH</td>_x000D_
        <td>III</td>_x000D_
        <td>JJJ</td>_x000D_
        <td>KKK</td>_x000D_
        <td>LLL</td>_x000D_
        <td>MMM</td>_x000D_
        <td>NNN</td>_x000D_
        <td>OOO</td>_x000D_
        <td>PPP</td>_x000D_
        <td>QQQ</td>_x000D_
      </tr>_x000D_
      <tr>_x000D_
        <td>15:00</td>_x000D_
        <td>AAA</td>_x000D_
        <td>BBB</td>_x000D_
        <td>CCC</td>_x000D_
        <td>DDD</td>_x000D_
        <td>EEE</td>_x000D_
        <td>FFF</td>_x000D_
        <td>GGG</td>_x000D_
        <td>HHH</td>_x000D_
        <td>III</td>_x000D_
        <td>JJJ</td>_x000D_
        <td>KKK</td>_x000D_
        <td>LLL</td>_x000D_
        <td>MMM</td>_x000D_
        <td>NNN</td>_x000D_
        <td>OOO</td>_x000D_
        <td>PPP</td>_x000D_
        <td>QQQ</td>_x000D_
      </tr>_x000D_
      <tr>_x000D_
        <td>16:00</td>_x000D_
        <td>AAA</td>_x000D_
        <td>BBB</td>_x000D_
        <td>CCC</td>_x000D_
        <td>DDD</td>_x000D_
        <td>EEE</td>_x000D_
        <td>FFF</td>_x000D_
        <td>GGG</td>_x000D_
        <td>HHH</td>_x000D_
        <td>III</td>_x000D_
        <td>JJJ</td>_x000D_
        <td>KKK</td>_x000D_
        <td>LLL</td>_x000D_
        <td>MMM</td>_x000D_
        <td>NNN</td>_x000D_
        <td>OOO</td>_x000D_
        <td>PPP</td>_x000D_
        <td>QQQ</td>_x000D_
      </tr>_x000D_
      <tr>_x000D_
        <td>17:00</td>_x000D_
        <td>AAA</td>_x000D_
        <td>BBB</td>_x000D_
        <td>CCC</td>_x000D_
        <td>DDD</td>_x000D_
        <td>EEE</td>_x000D_
        <td>FFF</td>_x000D_
        <td>GGG</td>_x000D_
        <td>HHH</td>_x000D_
        <td>III</td>_x000D_
        <td>JJJ</td>_x000D_
        <td>KKK</td>_x000D_
        <td>LLL</td>_x000D_
        <td>MMM</td>_x000D_
        <td>NNN</td>_x000D_
        <td>OOO</td>_x000D_
        <td>PPP</td>_x000D_
        <td>QQQ</td>_x000D_
      </tr>_x000D_
    </tbody>_x000D_
    </table>_x000D_
</div>
_x000D_
_x000D_
_x000D_


//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>;
}

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;
}

Add this in the head:

<link rel="stylesheet" type="text/css" href="/path/to/easyscrolltable.css">
<script src="/path/to/easyscrolltable.js"></script>

Javascript:

$('table.ytable').EasyScrollableTable({
    'top'  : 1,  
    'left' : 1,  
    'class': '',
    'width': '100%',
    'height': 'auto',
    'footer': false,
    'hover': true
});

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.


_x000D_
_x000D_
$(document).ready(function() {_x000D_
    var table = $('#example').DataTable( {_x000D_
        scrollY:        "400px",_x000D_
        scrollX:        true,_x000D_
        scrollCollapse: true,_x000D_
        paging:         true,_x000D_
        fixedColumns:   {_x000D_
            leftColumns: 3_x000D_
        }_x000D_
    } );_x000D_
} );
_x000D_
<head>_x000D_
 <title>table</title>_x000D_
 _x000D_
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>_x000D_
<link rel="stylesheet" href="https://cdn.datatables.net/1.10.16/css/jquery.dataTables.min.css">_x000D_
<link rel="stylesheet" href="https://cdn.datatables.net/fixedcolumns/3.2.4/css/fixedColumns.dataTables.min.css">_x000D_
<script type="text/javascript" src="http://cdn.datatables.net/1.10.2/js/jquery.dataTables.min.js"></script>_x000D_
<script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.js"></script>_x000D_
<script type="text/javascript" src="https://cdn.datatables.net/1.10.16/js/jquery.dataTables.min.js"></script>_x000D_
<script type="text/javascript" src="https://cdn.datatables.net/fixedcolumns/3.2.4/js/dataTables.fixedColumns.min.js"></script>_x000D_
_x000D_
_x000D_
<style>_x000D_
       th, td { white-space: nowrap; }_x000D_
    div.dataTables_wrapper {_x000D_
        width: 900px;_x000D_
        margin: 0 auto;_x000D_
    }_x000D_
</style>_x000D_
_x000D_
</head>
_x000D_
<table id="example" class="stripe row-border order-column" style="width:100%">_x000D_
        <thead>_x000D_
            <tr>_x000D_
                <th>First name</th>_x000D_
                <th>Last name</th>_x000D_
                <th>Position</th>_x000D_
                <th>Office</th>_x000D_
                <th>Age</th>_x000D_
                <th>Start date</th>_x000D_
                <th>Salary</th>_x000D_
                <th>Extn.</th>_x000D_
                <th>E-mail</th>_x000D_
            </tr>_x000D_
        </thead>_x000D_
        <tbody>_x000D_
            <tr>_x000D_
                <td>Tiger</td>_x000D_
                <td>Nixon</td>_x000D_
                <td>System Architect</td>_x000D_
                <td>Edinburgh</td>_x000D_
                <td>61</td>_x000D_
                <td>2011/04/25</td>_x000D_
                <td>$320,800</td>_x000D_
                <td>5421</td>_x000D_
                <td>[email protected]</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
                <td>Garrett</td>_x000D_
                <td>Winters</td>_x000D_
                <td>Accountant</td>_x000D_
                <td>Tokyo</td>_x000D_
                <td>63</td>_x000D_
                <td>2011/07/25</td>_x000D_
                <td>$170,750</td>_x000D_
                <td>8422</td>_x000D_
                <td>[email protected]</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
                <td>Ashton</td>_x000D_
                <td>Cox</td>_x000D_
                <td>Junior Technical Author</td>_x000D_
                <td>San Francisco</td>_x000D_
                <td>66</td>_x000D_
                <td>2009/01/12</td>_x000D_
                <td>$86,000</td>_x000D_
                <td>1562</td>_x000D_
                <td>[email protected]</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
                <td>Cedric</td>_x000D_
                <td>Kelly</td>_x000D_
                <td>Senior Javascript Developer</td>_x000D_
                <td>Edinburgh</td>_x000D_
                <td>22</td>_x000D_
                <td>2012/03/29</td>_x000D_
                <td>$433,060</td>_x000D_
                <td>6224</td>_x000D_
                <td>[email protected]</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
                <td>Airi</td>_x000D_
                <td>Satou</td>_x000D_
                <td>Accountant</td>_x000D_
                <td>Tokyo</td>_x000D_
                <td>33</td>_x000D_
                <td>2008/11/28</td>_x000D_
                <td>$162,700</td>_x000D_
                <td>5407</td>_x000D_
                <td>[email protected]</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
                <td>Brielle</td>_x000D_
                <td>Williamson</td>_x000D_
                <td>Integration Specialist</td>_x000D_
                <td>New York</td>_x000D_
                <td>61</td>_x000D_
                <td>2012/12/02</td>_x000D_
                <td>$372,000</td>_x000D_
                <td>4804</td>_x000D_
                <td>[email protected]</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
                <td>Herrod</td>_x000D_
                <td>Chandler</td>_x000D_
                <td>Sales Assistant</td>_x000D_
                <td>San Francisco</td>_x000D_
                <td>59</td>_x000D_
                <td>2012/08/06</td>_x000D_
                <td>$137,500</td>_x000D_
                <td>9608</td>_x000D_
                <td>[email protected]</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
                <td>Rhona</td>_x000D_
                <td>Davidson</td>_x000D_
                <td>Integration Specialist</td>_x000D_
                <td>Tokyo</td>_x000D_
                <td>55</td>_x000D_
                <td>2010/10/14</td>_x000D_
                <td>$327,900</td>_x000D_
                <td>6200</td>_x000D_
                <td>[email protected]</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
                <td>Colleen</td>_x000D_
                <td>Hurst</td>_x000D_
                <td>Javascript Developer</td>_x000D_
                <td>San Francisco</td>_x000D_
                <td>39</td>_x000D_
                <td>2009/09/15</td>_x000D_
                <td>$205,500</td>_x000D_
                <td>2360</td>_x000D_
                <td>[email protected]</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
                <td>Sonya</td>_x000D_
                <td>Frost</td>_x000D_
                <td>Software Engineer</td>_x000D_
                <td>Edinburgh</td>_x000D_
                <td>23</td>_x000D_
                <td>2008/12/13</td>_x000D_
                <td>$103,600</td>_x000D_
                <td>1667</td>_x000D_
                <td>[email protected]</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
                <td>Jena</td>_x000D_
                <td>Gaines</td>_x000D_
                <td>Office Manager</td>_x000D_
                <td>London</td>_x000D_
                <td>30</td>_x000D_
                <td>2008/12/19</td>_x000D_
                <td>$90,560</td>_x000D_
                <td>3814</td>_x000D_
                <td>[email protected]</td>_x000D_
            </tr>_x000D_
             <tr>_x000D_
                <td>Sakura</td>_x000D_
                <td>Yamamoto</td>_x000D_
                <td>Support Engineer</td>_x000D_
                <td>Tokyo</td>_x000D_
                <td>37</td>_x000D_
                <td>2009/08/19</td>_x000D_
                <td>$139,575</td>_x000D_
                <td>9383</td>_x000D_
                <td>[email protected]</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
                <td>Thor</td>_x000D_
                <td>Walton</td>_x000D_
                <td>Developer</td>_x000D_
                <td>New York</td>_x000D_
                <td>61</td>_x000D_
                <td>2013/08/11</td>_x000D_
                <td>$98,540</td>_x000D_
                <td>8327</td>_x000D_
                <td>[email protected]</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
                <td>Finn</td>_x000D_
                <td>Camacho</td>_x000D_
                <td>Support Engineer</td>_x000D_
                <td>San Francisco</td>_x000D_
                <td>47</td>_x000D_
                <td>2009/07/07</td>_x000D_
                <td>$87,500</td>_x000D_
                <td>2927</td>_x000D_
                <td>[email protected]</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
                <td>Serge</td>_x000D_
                <td>Baldwin</td>_x000D_
                <td>Data Coordinator</td>_x000D_
                <td>Singapore</td>_x000D_
                <td>64</td>_x000D_
                <td>2012/04/09</td>_x000D_
                <td>$138,575</td>_x000D_
                <td>8352</td>_x000D_
                <td>[email protected]</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
                <td>Zenaida</td>_x000D_
                <td>Frank</td>_x000D_
                <td>Software Engineer</td>_x000D_
                <td>New York</td>_x000D_
                <td>63</td>_x000D_
                <td>2010/01/04</td>_x000D_
                <td>$125,250</td>_x000D_
                <td>7439</td>_x000D_
                <td>[email protected]</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
                <td>Zorita</td>_x000D_
                <td>Serrano</td>_x000D_
                <td>Software Engineer</td>_x000D_
                <td>San Francisco</td>_x000D_
                <td>56</td>_x000D_
                <td>2012/06/01</td>_x000D_
                <td>$115,000</td>_x000D_
                <td>4389</td>_x000D_
                <td>[email protected]</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
                <td>Jennifer</td>_x000D_
                <td>Acosta</td>_x000D_
                <td>Junior Javascript Developer</td>_x000D_
                <td>Edinburgh</td>_x000D_
                <td>43</td>_x000D_
                <td>2013/02/01</td>_x000D_
                <td>$75,650</td>_x000D_
                <td>3431</td>_x000D_
                <td>[email protected]</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
                <td>Cara</td>_x000D_
                <td>Stevens</td>_x000D_
                <td>Sales Assistant</td>_x000D_
                <td>New York</td>_x000D_
                <td>46</td>_x000D_
                <td>2011/12/06</td>_x000D_
                <td>$145,600</td>_x000D_
                <td>3990</td>_x000D_
                <td>[email protected]</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
                <td>Hermione</td>_x000D_
                <td>Butler</td>_x000D_
                <td>Regional Director</td>_x000D_
                <td>London</td>_x000D_
                <td>47</td>_x000D_
                <td>2011/03/21</td>_x000D_
                <td>$356,250</td>_x000D_
                <td>1016</td>_x000D_
                <td>[email protected]</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
                <td>Lael</td>_x000D_
                <td>Greer</td>_x000D_
                <td>Systems Administrator</td>_x000D_
                <td>London</td>_x000D_
                <td>21</td>_x000D_
                <td>2009/02/27</td>_x000D_
                <td>$103,500</td>_x000D_
                <td>6733</td>_x000D_
                <td>[email protected]</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
                <td>Jonas</td>_x000D_
                <td>Alexander</td>_x000D_
                <td>Developer</td>_x000D_
                <td>San Francisco</td>_x000D_
                <td>30</td>_x000D_
                <td>2010/07/14</td>_x000D_
                <td>$86,500</td>_x000D_
                <td>8196</td>_x000D_
                <td>[email protected]</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
                <td>Shad</td>_x000D_
                <td>Decker</td>_x000D_
                <td>Regional Director</td>_x000D_
                <td>Edinburgh</td>_x000D_
                <td>51</td>_x000D_
                <td>2008/11/13</td>_x000D_
                <td>$183,000</td>_x000D_
                <td>6373</td>_x000D_
                <td>[email protected]</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
                <td>Michael</td>_x000D_
                <td>Bruce</td>_x000D_
                <td>Javascript Developer</td>_x000D_
                <td>Singapore</td>_x000D_
                <td>29</td>_x000D_
                <td>2011/06/27</td>_x000D_
                <td>$183,000</td>_x000D_
                <td>5384</td>_x000D_
                <td>[email protected]</td>_x000D_
            </tr>_x000D_
            <tr>_x000D_
                <td>Donna</td>_x000D_
                <td>Snider</td>_x000D_
                <td>Customer Support</td>_x000D_
                <td>New York</td>_x000D_
                <td>27</td>_x000D_
                <td>2011/01/25</td>_x000D_
                <td>$112,000</td>_x000D_
                <td>4226</td>_x000D_
                <td>[email protected]</td>_x000D_
            </tr>_x000D_
        </tbody>_x000D_
    </table>
_x000D_
_x000D_
_x000D_

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.


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

_x000D_
_x000D_
let l  = 0;_x000D_
let t  = 0;_x000D_
_x000D_
const MouseWheelHandler = (e) => {_x000D_
  // vertical scroll_x000D_
  if (e.deltaX == -0) {_x000D_
    // t = t - e.deltaY_x000D_
_x000D_
  // horizonal scroll_x000D_
  } else if (e.deltaY == -0) {_x000D_
    l = l - e.deltaX_x000D_
    if (l >= 0) {_x000D_
      l = 0;_x000D_
      document.getElementById("gantt_task").style.transform = "translateX(1px)"_x000D_
      document.getElementById("gantt_task_header").style.transform = "translateX(1px)"_x000D_
      return false_x000D_
    } _x000D_
    document.getElementById("gantt_task").style.transform = "translateX(" + l.toString() + "px)"_x000D_
    document.getElementById("gantt_task_header").style.transform = "translateX(" + l.toString() + "px)"_x000D_
  }_x000D_
  return false;_x000D_
}_x000D_
_x000D_
window.addEventListener("wheel", MouseWheelHandler, false);
_x000D_
.row {_x000D_
  border-bottom: 1px solid #979A9A_x000D_
}_x000D_
#gantt_grid_header {_x000D_
  height:   30px;_x000D_
  width:    100px;_x000D_
  position: fixed;_x000D_
  z-index:  3;_x000D_
  top:      0px;_x000D_
  left:     0px;_x000D_
  border:   1px solid #cecece;_x000D_
  background-color: #F08080;_x000D_
}     _x000D_
_x000D_
#gantt_task_header {_x000D_
  height:   30px;_x000D_
  width:    400px;_x000D_
  position: fixed;_x000D_
  z-index:  2;_x000D_
  top:      0px;_x000D_
  left:     100px;_x000D_
  border:   1px solid #cecece;_x000D_
  background-color: #FFC300;_x000D_
}_x000D_
_x000D_
#gantt_grid {_x000D_
  width:    100px; _x000D_
  height:   400px;_x000D_
  position: absolute;_x000D_
  left:     0px;_x000D_
  top:      0px;_x000D_
  z-index:  1;_x000D_
  border:   1px solid #cecece;_x000D_
  background-color: #DAF7A6;_x000D_
}_x000D_
_x000D_
#gantt_task {_x000D_
  width:    400px; _x000D_
  height:   400px;_x000D_
  position: absolute;_x000D_
  left:     100px;_x000D_
  top:      0px;_x000D_
  border:   1px solid #cecece;_x000D_
  background-color: #FF5733;_x000D_
}
_x000D_
<html>_x000D_
    <div id="gantt_grid_header">_x000D_
      HEADER_x000D_
    </div>_x000D_
    <div id="gantt_grid">_x000D_
      <div class="row">V Scroll OK</div>_x000D_
      <div class="row">V Scroll OK</div>_x000D_
      <div class="row">V Scroll OK</div>_x000D_
      <div class="row">V Scroll OK</div>_x000D_
      <div class="row">V Scroll OK</div>_x000D_
      <div class="row">V Scroll OK</div>_x000D_
      <div class="row">V Scroll OK</div>_x000D_
      <div class="row">V Scroll OK</div>_x000D_
      <div class="row">V Scroll OK</div>_x000D_
    </div>_x000D_
    <div id="gantt_task_header">_x000D_
      DATA HEADER_x000D_
    </div>_x000D_
    <div id="gantt_task">_x000D_
      <div class="row">Vertical,Horizenal Scroll OK</div>_x000D_
      <div class="row">Vertical,Horizenal Scroll OK</div>_x000D_
      <div class="row">Vertical,Horizenal Scroll OK</div>_x000D_
      <div class="row">Vertical,Horizenal Scroll OK</div>_x000D_
      <div class="row">Vertical,Horizenal Scroll OK</div>_x000D_
      <div class="row">Vertical,Horizenal Scroll OK</div>_x000D_
      <div class="row">Vertical,Horizenal Scroll OK</div>_x000D_
      <div class="row">Vertical,Horizenal Scroll OK</div>_x000D_
      <div class="row">Vertical,Horizenal Scroll OK</div>_x000D_
    </div>_x000D_
</html>
_x000D_
_x000D_
_x000D_


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.


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>

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:

_x000D_
_x000D_
// requires jquery library_x000D_
jQuery(document).ready(function() {_x000D_
  jQuery(".main-table").clone(true).appendTo('#table-scroll').addClass('clone');   _x000D_
 });
_x000D_
  .table-scroll {_x000D_
    position:relative;_x000D_
    max-width:600px;_x000D_
    margin:auto;_x000D_
    overflow:hidden;_x000D_
    border:1px solid #000;_x000D_
  }_x000D_
.table-wrap {_x000D_
 width:100%;_x000D_
 overflow:auto;_x000D_
}_x000D_
.table-scroll table {_x000D_
 width:100%;_x000D_
 margin:auto;_x000D_
 border-collapse:separate;_x000D_
 border-spacing:0;_x000D_
}_x000D_
.table-scroll th, .table-scroll td {_x000D_
 padding:5px 10px;_x000D_
 border:1px solid #000;_x000D_
 background:#fff;_x000D_
 white-space:nowrap;_x000D_
 vertical-align:top;_x000D_
}_x000D_
.table-scroll thead, .table-scroll tfoot {_x000D_
 background:#f9f9f9;_x000D_
}_x000D_
.clone {_x000D_
 position:absolute;_x000D_
 top:0;_x000D_
 left:0;_x000D_
 pointer-events:none;_x000D_
}_x000D_
.clone th, .clone td {_x000D_
 visibility:hidden_x000D_
}_x000D_
.clone td, .clone th {_x000D_
 border-color:transparent_x000D_
}_x000D_
.clone tbody th {_x000D_
 visibility:visible;_x000D_
 color:red;_x000D_
}_x000D_
.clone .fixed-side {_x000D_
 border:1px solid #000;_x000D_
 background:#eee;_x000D_
 visibility:visible;_x000D_
}_x000D_
.clone thead, .clone tfoot{background:transparent;}
_x000D_
<div id="table-scroll" class="table-scroll">_x000D_
  <div class="table-wrap">_x000D_
    <table class="main-table">_x000D_
      <thead>_x000D_
        <tr>_x000D_
          <th class="fixed-side" scope="col">&nbsp;</th>_x000D_
          <th scope="col">Header 2</th>_x000D_
          <th scope="col">Header 3</th>_x000D_
          <th scope="col">Header 4</th>_x000D_
          <th scope="col">Header 5</th>_x000D_
          <th scope="col">Header 6</th>_x000D_
          <th scope="col">Header 7</th>_x000D_
          <th scope="col">Header 8</th>_x000D_
        </tr>_x000D_
      </thead>_x000D_
      <tbody>_x000D_
        <tr>_x000D_
          <th class="fixed-side">Left Column</th>_x000D_
          <td>Cell content<br>_x000D_
            test</td>_x000D_
          <td><a href="#">Cell content longer</a></td>_x000D_
          <td>Cell content</td>_x000D_
          <td>Cell content</td>_x000D_
          <td>Cell content</td>_x000D_
          <td>Cell content</td>_x000D_
          <td>Cell content</td>_x000D_
        </tr>_x000D_
        <tr>_x000D_
          <th class="fixed-side">Left Column</th>_x000D_
          <td>Cell content</td>_x000D_
          <td>Cell content longer</td>_x000D_
          <td>Cell content</td>_x000D_
          <td>Cell content</td>_x000D_
          <td>Cell content</td>_x000D_
          <td>Cell content</td>_x000D_
          <td>Cell content</td>_x000D_
        </tr>_x000D_
        <tr>_x000D_
          <th class="fixed-side">Left Column</th>_x000D_
          <td>Cell content</td>_x000D_
          <td>Cell content longer</td>_x000D_
          <td>Cell content</td>_x000D_
          <td>Cell content</td>_x000D_
          <td>Cell content</td>_x000D_
          <td>Cell content</td>_x000D_
          <td>Cell content</td>_x000D_
        </tr>_x000D_
        <tr>_x000D_
          <th class="fixed-side">Left Column</th>_x000D_
          <td>Cell content</td>_x000D_
          <td>Cell content longer</td>_x000D_
          <td>Cell content</td>_x000D_
          <td>Cell content</td>_x000D_
          <td>Cell content</td>_x000D_
          <td>Cell content</td>_x000D_
          <td>Cell content</td>_x000D_
        </tr>_x000D_
        <tr>_x000D_
          <th class="fixed-side">Left Column</th>_x000D_
          <td>Cell content</td>_x000D_
          <td>Cell content longer</td>_x000D_
          <td>Cell content</td>_x000D_
          <td>Cell content</td>_x000D_
          <td>Cell content</td>_x000D_
          <td>Cell content</td>_x000D_
          <td>Cell content</td>_x000D_
        </tr>_x000D_
        <tr>_x000D_
          <th class="fixed-side">Left Column</th>_x000D_
          <td>Cell content</td>_x000D_
          <td>Cell content longer</td>_x000D_
          <td>Cell content</td>_x000D_
          <td>Cell content</td>_x000D_
          <td>Cell content</td>_x000D_
          <td>Cell content</td>_x000D_
          <td>Cell content</td>_x000D_
        </tr>_x000D_
      </tbody>_x000D_
      <tfoot>_x000D_
        <tr>_x000D_
          <th class="fixed-side">&nbsp;</th>_x000D_
          <td>Footer 2</td>_x000D_
          <td>Footer 3</td>_x000D_
          <td>Footer 4</td>_x000D_
          <td>Footer 5</td>_x000D_
          <td>Footer 6</td>_x000D_
          <td>Footer 7</td>_x000D_
          <td>Footer 8</td>_x000D_
        </tr>_x000D_
      </tfoot>_x000D_
    </table>_x000D_
  </div>_x000D_
</div>_x000D_
_x000D_
<p>See <a href="https://codepen.io/paulobrien/pen/LBrMxa" target="blank">position Sticky version </a>with no JS</p>_x000D_
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js" type="text/javascript"></script>
_x000D_
_x000D_
_x000D_


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.


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

_x000D_
_x000D_
.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;
}
_x000D_
<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>
_x000D_
_x000D_
_x000D_

Bootply code: https://www.bootply.com/g8pfBXOcY9


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

<style type="text/css">
            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;
            }
        </style>
        <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>

Examples related to html

Embed ruby within URL : Middleman Blog Please help me convert this script to a simple image slider Generating a list of pages (not posts) without the index file Why there is this "clear" class before footer? Is it possible to change the content HTML5 alert messages? Getting all files in directory with ajax DevTools failed to load SourceMap: Could not load content for chrome-extension How to set width of mat-table column in angular? How to open a link in new tab using angular? ERROR Error: Uncaught (in promise), Cannot match any routes. URL Segment

Examples related to css

need to add a class to an element Using Lato fonts in my css (@font-face) Please help me convert this script to a simple image slider Why there is this "clear" class before footer? How to set width of mat-table column in angular? Center content vertically on Vuetify bootstrap 4 file input doesn't show the file name Bootstrap 4: responsive sidebar menu to top navbar Stylesheet not loaded because of MIME-type Force flex item to span full row width

Examples related to html-table

Table column sizing DataTables: Cannot read property 'length' of undefined TypeError: a bytes-like object is required, not 'str' in python and CSV How to get the <td> in HTML tables to fit content, and let a specific <td> fill in the rest How to stick table header(thead) on top while scrolling down the table rows with fixed header(navbar) in bootstrap 3? Sorting table rows according to table header column using javascript or jquery How to make background of table cell transparent How to auto adjust table td width from the content bootstrap responsive table content wrapping How to print table using Javascript?