how do I create an HTML table with fixed/frozen left column and scrollable body?

    |
  • Added:
  • |
  • In: Wordpress

How do I create an HTML table with fixed/frozen left column and 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 Has 16 Answeres | Orginal Question | agsamek

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.

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>

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

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>

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> 

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.

FWIW, here is a table that is scrollable with the head and side fixed.

http://codepen.io/ajkochanowicz/pen/KHdih

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

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> 

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; 

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.

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.

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.

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 anyone!

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


Search
I am...

Sajjad Hossain

I have five years of experience in web development sector. I love to do amazing projects and share my knowledge with all.

NEED HOSTING?
Are you searching for Good Quality hosting?
You can Try It!
Connect Social With PHPAns
Top