I have like 15 columns in the table. I want to scroll both horizontal and vertical and header should be fixed when I scroll vertical. I have tried various examples but no
Here is a pure javascript solution using TableAdjuster and TableData classes:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title></title>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style>
.hdrDiv
{
width: 100%;
overflow-x: hidden;
}
.tblDiv
{
overflow-y: auto;
overflow-x: hidden;
margin: 0;
padding: 0;
height: 500px;
}
.hdr
{
width: 100%;
}
.hdr td, .tbl td
{
padding-left: 8px;
padding-right: 8px;
vertical-align: middle;
border-style: solid;
border-width: 1px;
border-color: rgb(163,163,163);
text-align: left;
cursor: default;
margin: 0;
}
.hdr td
{
font: bold 12px Tahoma;
padding-top: 4px;
padding-bottom: 4px;
}
.hdr td.col1
{
width: 20%;
}
.hdr td.col2
{
width: 20%;
}
.hdr td.col3
{
width: 25%;
}
.hdr td.col4
{
width: 35%;
}
.hdr, .tbl
{
table-layout: fixed;
min-width: 400px;
}
.tbl td
{
font: 12px Tahoma;
}
</style>
<script type="text/javascript">
var g_adjuster = null;
var g_initRowCount = 4;
function getScrollbarWidth()
{
var outer = document.createElement("div");
outer.style.visibility = "hidden";
outer.style.width = "100px";
document.body.appendChild(outer);
var widthNoScroll = outer.offsetWidth;
outer.style.overflow = "scroll";
var inner = document.createElement("div");
inner.style.width = "100%";
outer.appendChild(inner);
var widthWithScroll = inner.offsetWidth;
outer.parentNode.removeChild(outer);
return widthNoScroll - widthWithScroll;
}
function ExtractInt(value)
{
var regExp = /([a-zA-Z])/g;
value = value.replace(regExp, "");
return (value.length == 0) ? 0 : parseInt(value);
}
function TableData(hdrID, tableID)
{
this.m_hdrID = hdrID;
this.m_tableID = tableID;
this.m_pbWidth = 0;
var header = document.getElementById(this.m_hdrID);
var table = document.getElementById(this.m_tableID);
this.m_hasSB = table ? (table.parentNode.scrollHeight > table.parentNode.clientHeight) : false;
var hdrRow = (header != null && header.rows.length > 0) ? header.rows[0] : null;
var clsName = !hdrRow ? "" : (hdrRow.className.length > 0) ? hdrRow.className : (header.className.length > 0) ? header.className + " td" : "";
var elements = [];
elements.push({ prop: 'padding-left', value: ''});
elements.push({ prop: 'padding-right', value: ''});
elements.push({ prop: 'border-width', value: ''});
this.GetCSSValues('.' + clsName, elements);
for (var i=0; i<elements.length; i++)
{
var w = ExtractInt(elements[i].value);
if (elements[i].prop == 'border-width')
w *= 2;
this.m_pbWidth += w;
}
}
TableData.prototype.GetCSSValues =
function(theClass, elements)
{
var classLower = theClass.toLowerCase();
var cssRules;
for (var i = 0; i < document.styleSheets.length; i++)
{
var found = true;
try
{
if (document.styleSheets[i]['rules'])
cssRules = 'rules';
else if (document.styleSheets[i]['cssRules'])
cssRules = 'cssRules';
else
{
found = false;
}
}
catch(err)
{
break;
}
if (!found)
continue;
for (var j = 0; j < document.styleSheets[i][cssRules].length; j++)
{
if (typeof document.styleSheets[i][cssRules][j].selectorText != 'string')
continue;
var selectorLower = document.styleSheets[i][cssRules][j].selectorText.toLowerCase();
if (selectorLower.indexOf(classLower) >= 0)
{
for (var k=0; k<elements.length; k++)
{
var v = document.styleSheets[i][cssRules][j].style.getPropertyValue(elements[k].prop);
if (typeof v == 'string' && v.length > 0)
elements[k].value = v;
}
}
}
}
}
TableData.prototype.Adjust =
function(sbWidth)
{
var header = document.getElementById(this.m_hdrID);
var table = document.getElementById(this.m_tableID);
var hdrRow = (header != null && header.rows.length > 0) ? header.rows[0] : null;
if (!hdrRow || !table)
return;
var hasSB = table.parentNode.scrollHeight > table.parentNode.clientHeight;
header.style.width = hasSB ? "calc(100% - " + sbWidth.toString() + "px)" : "100%";
var colCount = hdrRow.cells.length;
for (var i=0; i<table.rows.length; i++)
{
var r = table.rows[i];
for (var j=0; j<r.cells.length; j++)
{
if (j >= colCount)
break;
var w = hdrRow.cells[j].offsetWidth - this.m_pbWidth;
r.cells[j].style.width = w + 'px';
}
}
}
function TableAdjuster()
{
this.m_sbWidth = getScrollbarWidth();
this.m_data = [];
}
TableAdjuster.prototype.AddTable =
function(hdrID, tableID)
{
// We can have multiple scrollable tables on the page
this.m_data.push(new TableData(hdrID, tableID));
}
TableAdjuster.prototype.Adjust =
function()
{
for (var i=0; i<this.m_data.length; i++)
this.m_data[i].Adjust(this.m_sbWidth);
}
function DeleteRow()
{
var table = document.getElementById("tablebody");
if (table != null && table.rows.length > 0)
table.deleteRow(table.rows.length-1);
AdjustSize();
}
function AddRow(adjust)
{
var header = document.getElementById("header");
var table = document.getElementById("tablebody");
var hdrRow = (header != null && header.rows.length > 0) ? header.rows[0] : null;
if (!hdrRow || !table)
return;
var colCount = hdrRow.cells.length;
var rowNum = table.rows.length + 1;
var r = table.insertRow(-1);
for (var i=0; i<colCount; i++)
{
var c = r.insertCell(-1);
c.innerHTML = "Row " + rowNum.toString() + " Column " + (i + 1).toString() + " content";
}
if (adjust)
AdjustSize();
}
function AdjustSize()
{
g_adjuster.Adjust();
}
function InitPage()
{
for (var i=0; i<g_initRowCount; i++)
AddRow(false);
g_adjuster = new TableAdjuster();
g_adjuster.AddTable("header", "tablebody");
AdjustSize();
}
</script>
</head>
<body onload="InitPage()" onresize="AdjustSize()">
<div id="headerDiv" class="hdrDiv">
<table id="header" class="hdr">
<tr>
<td class="col1">Column 1 Title</td>
<td class="col2">Column 2 Title</td>
<td class="col3">Column 3 Title</td>
<td class="col4">Column 4 Title</td>
</tr>
</table>
</div>
<div id="tableDiv" class="tblDiv">
<table id="tablebody" class="tbl">
</table>
</div>
<div style="margin-top: 12px">
<input id="Button1" type="button" value="Add Row" onclick="AddRow(true);"/>
<input id="Button2" type="button" value="Delete Row" onclick="DeleteRow();"/>
</div>
</body>
</html>
I have found a system that is
Result:
HTML:
<thead>
<tr>
<th class="nombre"><%= f.label :cost_center %></th>
<th class="cabecera cc">Personal</th>
<th class="cabecera cc">Dpto</th>
</tr>
</thead>
<tbody>
<% @cost_centers.each do |cc| %>
<tr>
<td class="nombre"><%= cc.nombre_corto %></td>
<td class="cc"><%= cc.cacentrocoste %></td>
<td class="cc"><%= cc.cacentrocoste_dpto %></td>
</tr>
<% end %>
</tbody>
<tfoot>
<tr>
<td colspan="3"><a href="#">Mostrar mas usuarios</a></td>
</tr>
</tfoot>
</table>
CSS:
div.cost_center{
font-size:75%;
margin-left:5px;
margin-top:5px;
margin-bottom: 2px;
float: right;
display: inline-block;
overflow-y: auto;
overflow-x: hidden;
max-height:300px;
}
div.cost_center label {
float:none;
font-size:14px;
}
div.cost_center table{
width:300px;
border-collapse: collapse;
float:right;
table-layout:fixed;
}
div.cost_center table tr{
height:16px;
}
div.cost_center th{
font-weight:normal;
}
div.cost_center table tbody{
display: block;
overflow: auto;
max-height:240px;
}
div.cost_center table thead{
display:block;
}
div.cost_center table tfoot{
display:block;
}
div.cost_center table tfoot td{
width:280px;
}
div.cost_center .cc{
width:60px;
text-align: center;
border: 1px solid #999;
}
div.cost_center .nombre{
width:150px;
}
div.cost_center tbody .nombre{
border: 1px solid #999;
}
div.cost_center table tfoot td{
text-align:center;
border: 1px solid #999;
}
div.cost_center table th,
div.cost_center table td {
padding: 2px;
vertical-align: middle;
}
div.cost_center table tbody td {
white-space: normal;
font: .8em/1.4em Verdana, sans-serif;
color: #000;
background-color: white;
}
div.cost_center table th.cabecera {
font: 0.8em/1.4em Verdana, sans-serif;
color: #000;
background-color: #FFEAB5;
}
what you could do is a bit of visual trick to achieve this, use two div tags
<div class="Headers">
<table class="NewHeader">
<tr>
</tr>
</table>
</div>
<div class="Table">
<table class="MyTable">
<tr>
<th>
</th>
...
</tr>
<tr>
<td>
</td>
...
</tr>
</div>
now with a bit of JavaScript or JQuery you can get the th, set its width to match the cell width, and move the th cell to the "Headers" table
$(document).ready(function(){
var counter = 0;
$(".MyTable th").each(function(){
var width = $('.MyTable tr:last td:eq(' + counter + ')').width();
$(".NewHeader tr").append(this);
this.width = width;
counter++;
});
});
now only is left to do is to style the div "Table" with overflow, so now if you would scroll the second table the header will remain in place, i used jquery to simplify the readability, but can be done in JavaScript is same way
Live Demo
Example with automatic vertical scroll body and header
It's easily acheived with css. It all comes down to the following:
table {
overflow-x:scroll;
}
tbody {
max-height: /*your desired max height*/
overflow-y:scroll;
display:block;
}
Updated for IE9 JSFiddle example
You should use some third party table, like YUI table
I have created a pure CSS solution to this problem that may work better for some people than the accepted answer (which I can't seem to get working at all). Unlike most of the other scrolling-body tables I've seen that don't require fixed widths, mine has a minimum width that is determined by its content. It will wrap text if possible to avoid overflowing its container (except in the headers, which don't allow soft-wrapping, unfortunately), but once the wrapping opportunities are used up, it won't get any narrower. That forces the parent element (usually the body tag) to handle the horizontal scrolling, which keeps the headers and columns in sync.
Here's a fiddle.
Here's the code:
HTML
<div class="scrollingtable">
<div>
<div>
<table>
<caption>Top Caption</caption>
<thead>
<tr>
<th><div label="Column 1"/></th>
<th><div label="Column 2"/></th>
<th><div label="Column 3"/></th>
<th>
<!--more versatile way of doing column label; requires 2 identical copies of label-->
<div><div>Column 4</div><div>Column 4</div></div>
</th>
<th class="scrollbarhead"/> <!--ALWAYS ADD THIS EXTRA CELL AT END OF HEADER ROW-->
</tr>
</thead>
<tbody>
<tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
<tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
<tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
<tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
<tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
<tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
<tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
<tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
<tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
<tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
<tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
<tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
</tbody>
</table>
</div>
Faux bottom caption
</div>
</div>
CSS
<!--[if lte IE 9]><style>.scrollingtable > div > div > table {margin-right: 17px;}</style><![endif]-->
<style>
/*the following html and body rule sets are required only if using a % width or height*/
/*html {
width: 100%;
height: 100%;
}*/
body {
box-sizing: border-box;
width: 100%;
height: 100%;
margin: 0;
padding: 0 20px 0 20px;
text-align: center;
}
.scrollingtable {
box-sizing: border-box;
display: inline-block;
vertical-align: middle;
overflow: hidden;
width: auto; /*if you want a fixed width, set it here, else set to auto*/
min-width: 0/*100%*/; /*if you want a % width, set it here, else set to 0*/
height: 188px/*100%*/; /*set table height here; can be fixed value or %*/
min-height: 0/*104px*/; /*if using % height, make this large enough to fit scrollbar arrows + caption + thead*/
font-family: Verdana, Tahoma, sans-serif;
font-size: 16px;
line-height: 20px;
padding: 20px 0 20px 0; /*need enough padding to make room for caption*/
text-align: left;
}
.scrollingtable * {box-sizing: border-box;}
.scrollingtable > div {
position: relative;
border-top: 1px solid black;
height: 100%;
padding-top: 20px; /*this determines column header height*/
}
.scrollingtable > div:before {
top: 0;
background: cornflowerblue; /*header row background color*/
}
.scrollingtable > div:before,
.scrollingtable > div > div:after {
content: "";
position: absolute;
z-index: -1;
width: 100%;
height: 100%;
left: 0;
}
.scrollingtable > div > div {
min-height: 0/*43px*/; /*if using % height, make this large enough to fit scrollbar arrows*/
max-height: 100%;
overflow: scroll/*auto*/; /*set to auto if using fixed or % width; else scroll*/
overflow-x: hidden;
border: 1px solid black; /*border around table body*/
}
.scrollingtable > div > div:after {background: white;} /*match page background color*/
.scrollingtable > div > div > table {
width: 100%;
border-spacing: 0;
margin-top: -20px; /*inverse of column header height*/
margin-right: 17px; /*uncomment if using % width*/
}
.scrollingtable > div > div > table > caption {
position: absolute;
top: -20px; /*inverse of caption height*/
margin-top: -1px; /*inverse of border-width*/
width: 100%;
font-weight: bold;
text-align: center;
}
.scrollingtable > div > div > table > * > tr > * {padding: 0;}
.scrollingtable > div > div > table > thead {
vertical-align: bottom;
white-space: nowrap;
text-align: center;
}
.scrollingtable > div > div > table > thead > tr > * > div {
display: inline-block;
padding: 0 6px 0 6px; /*header cell padding*/
}
.scrollingtable > div > div > table > thead > tr > :first-child:before {
content: "";
position: absolute;
top: 0;
left: 0;
height: 20px; /*match column header height*/
border-left: 1px solid black; /*leftmost header border*/
}
.scrollingtable > div > div > table > thead > tr > * > div[label]:before,
.scrollingtable > div > div > table > thead > tr > * > div > div:first-child,
.scrollingtable > div > div > table > thead > tr > * + :before {
position: absolute;
top: 0;
white-space: pre-wrap;
color: white; /*header row font color*/
}
.scrollingtable > div > div > table > thead > tr > * > div[label]:before,
.scrollingtable > div > div > table > thead > tr > * > div[label]:after {content: attr(label);}
.scrollingtable > div > div > table > thead > tr > * + :before {
content: "";
display: block;
min-height: 20px; /*match column header height*/
padding-top: 1px;
border-left: 1px solid black; /*borders between header cells*/
}
.scrollingtable .scrollbarhead {float: right;}
.scrollingtable .scrollbarhead:before {
position: absolute;
width: 100px;
top: -1px; /*inverse border-width*/
background: white; /*match page background color*/
}
.scrollingtable > div > div > table > tbody > tr:after {
content: "";
display: table-cell;
position: relative;
padding: 0;
border-top: 1px solid black;
top: -1px; /*inverse of border width*/
}
.scrollingtable > div > div > table > tbody {vertical-align: top;}
.scrollingtable > div > div > table > tbody > tr {background: white;}
.scrollingtable > div > div > table > tbody > tr > * {
border-bottom: 1px solid black;
padding: 0 6px 0 6px;
height: 20px; /*match column header height*/
}
.scrollingtable > div > div > table > tbody:last-of-type > tr:last-child > * {border-bottom: none;}
.scrollingtable > div > div > table > tbody > tr:nth-child(even) {background: gainsboro;} /*alternate row color*/
.scrollingtable > div > div > table > tbody > tr > * + * {border-left: 1px solid black;} /*borders between body cells*/
And here's a post where I answered in a little more detail.