In making large tables for my Senate preference explorers, I wanted to freeze the top row and first two columns, as with a "freeze panes" in Excel. There are several ways of going about this, as various Stack Overflow pages attest; the one that my brain liked the most is based on this answer, but there were a couple of gotchas in implementing it, and this page explains them (as much for my personal reference as well as for any passing Googlers; a lot of the functions described in this page were new to me). A bare-bones page with a single table is here; I wanted a couple of extra details, as described below. It's a bit amusingly laggy on my Galaxy S3, but looks perfect on my laptop.
The basic idea is as follows:
.scrollTop
and .scrollLeft
to see how far the table has scrolled, then apply an
offsetting translate
to the cells we want to freeze.
What tripped me up:
background-color
set, then the content of the scrolling cells overlaps the frozen content and it's
a mess. I usually define a class called tr_shaded
and then use tr_shaded:nth_child(even)
and
tr_shaded:nth_child(odd)
to alternate the background-color
of the rows in my tables, but this did not work for me, and I
needed to set it expicitly.
z-index
property in each cell (or class of cells) to specify the front-to-back order.
z-index
only takes effect when the elements have a position
property.
When writing up this page, I tried to make the code slightly more general, in which the series of freeze-paned tables' container div id's would be set in an array and looped over. This led me to:
Here are a couple of tables. One:
Corner | Col header | Col header | Col header | Col header |
---|---|---|---|---|
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
And now two:
Corner | Col header | Col header | Col header | Col header |
---|---|---|---|---|
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Row header | asdf asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf | asdf asdf asdf asdf asdf asdf |
Now the code. In the CSS (the first few aren't relevant, they're just to match the style of this page; the important ones start at the
scrolly_table
class):
table {
table-layout: fixed;
border-spacing: 0px;
}
td, th {
padding-left: 5px;
padding-right: 5px;
}
.tr_shaded:nth-child(even) {
background: #e0e0e0;
}
.tr_shaded:nth-child(odd) {
background: #ffffff;
}
.scrolly_table {
white-space: nowrap;
overflow: auto;
}
/* The frozen cells will each get two class names,
making it easier for me to select all of them or
only a subset. All frozen cells will be "fixed",
the corner will also be in class "freeze", and the
row and column headers will be "horizontal" and
"vertical" respectively. */
.fixed.freeze {
z-index: 10;
position: relative;
}
.fixed.freeze_vertical {
z-index: 5;
position: relative;
}
.fixed.freeze_horizontal {
z-index: 1;
position: relative;
}
window.innerHeight
.
<p>Here are a couple of tables. One:</p>
<div id="scrolling_table_1" class="scrolly_table" style="max-width:500px; max-height:200px">
<table>
<tr>
<th style="background-color:white" class="fixed freeze">Corner</th>
<th style="background-color:white" class="fixed freeze_vertical">Col header</th>
<th style="background-color:white" class="fixed freeze_vertical">Col header</th>
<th style="background-color:white" class="fixed freeze_vertical">Col header</th>
<th style="background-color:white" class="fixed freeze_vertical">Col header</th>
</tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
</table>
</div>
<p>And now two:</p>
<div id="scrolling_table_2" class="scrolly_table scrolling_table_2" style="max-width:500px; max-height:200px">
<table>
<tr>
<th style="background-color:white" class="fixed freeze">Corner</th>
<th style="background-color:white" class="fixed freeze_vertical">Col header</th>
<th style="background-color:white" class="fixed freeze_vertical">Col header</th>
<th style="background-color:white" class="fixed freeze_vertical">Col header</th>
<th style="background-color:white" class="fixed freeze_vertical">Col header</th>
</tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
<tr class="tr_shaded"><td class="fixed freeze_horizontal">Row header</td><td>asdf asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td><td>asdf asdf asdf asdf asdf asdf </td></tr>
</table>
</div>
Lastly, the JavaScript. There's a bit of work in here adding the container div id to the frozen cells class names (without doing this, the frozen
cells in all tables will get translated when you start scrolling in one table). There's also some rigmarole to set the background-color
of each row header so that it matches the tr_shaded
colours.
I joke about wondering what_is_this
because the Stack
Overflow answer I was working off uses this
, but it's clear from the addEventListener at the end that it's the container div.
function freeze_pane_listener(what_is_this, table_class) {
// Wrapping a function so that the listener can be defined
// in a loop over a set of scrolling table id's.
// Cf. http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example
return function() {
var i;
var translate_y = "translate(0," + what_is_this.scrollTop + "px)";
var translate_x = "translate(" + what_is_this.scrollLeft + "px,0px)";
var translate_xy = "translate(" + what_is_this.scrollLeft + "px," + what_is_this.scrollTop + "px)";
var fixed_vertical_elts = document.getElementsByClassName(table_class + " freeze_vertical");
var fixed_horizontal_elts = document.getElementsByClassName(table_class + " freeze_horizontal");
var fixed_both_elts = document.getElementsByClassName(table_class + " freeze");
// The webkitTransforms are for a set of ancient smartphones/browsers,
// one of which I have, so I code it for myself:
for (i = 0; i < fixed_horizontal_elts.length; i++) {
fixed_horizontal_elts[i].style.webkitTransform = translate_x;
fixed_horizontal_elts[i].style.transform = translate_x;
}
for (i = 0; i < fixed_vertical_elts.length; i++) {
fixed_vertical_elts[i].style.webkitTransform = translate_y;
fixed_vertical_elts[i].style.transform = translate_y;
}
for (i = 0; i < fixed_both_elts.length; i++) {
fixed_both_elts[i].style.webkitTransform = translate_xy;
fixed_both_elts[i].style.transform = translate_xy;
}
}
}
function even_odd_color(i) {
if (i % 2 == 0) {
return "#e0e0e0";
} else {
return "#ffffff";
}
}
function parent_id(wanted_node_name, elt) {
// Function to work up the DOM until it reaches
// an element of type wanted_node_name, and return
// that element's id.
var wanted_parent = parent_elt(wanted_node_name, elt);
if ((wanted_parent == undefined) || (wanted_parent.nodeName == null)) {
// Sad trombone noise.
return "";
} else {
return wanted_parent.id;
}
}
function parent_elt(wanted_node_name, elt) {
// Function to work up the DOM until it reaches
// an element of type wanted_node_name, and return
// that element.
var this_parent = elt.parentElement;
if ((this_parent == undefined) || (this_parent.nodeName == null)) {
// Sad trombone noise.
return null;
} else if (this_parent.nodeName == wanted_node_name) {
// Found it:
return this_parent;
} else {
// Recurse:
return parent_elt(wanted_node_name, this_parent);
}
}
var i, parent_div_id, parent_tr, table_i, scroll_div;
var scrolling_table_div_ids = ["scrolling_table_1", "scrolling_table_2"];
// This array will let us keep track of even/odd rows:
var scrolling_table_tr_counters = [];
for (i = 0; i < scrolling_table_div_ids.length; i++) {
scrolling_table_tr_counters.push(0);
}
// Append the parent div id to the class of each frozen element:
var fixed_elements = document.getElementsByClassName("fixed");
for (i = 0; i < fixed_elements.length; i++) {
fixed_elements[i].className += " " + parent_id("DIV", fixed_elements[i]);
}
// Set background colours of row headers, alternating according to
// even_odd_color(), which should have the same values as those
// defined in the CSS for the tr_shaded class.
var fixed_horizontal_elements = document.getElementsByClassName("freeze_horizontal");
for (i = 0; i < fixed_horizontal_elements.length; i++) {
parent_div_id = parent_id("DIV", fixed_horizontal_elements[i]);
table_i = scrolling_table_div_ids.indexOf(parent_div_id);
if (table_i >= 0) {
parent_tr = parent_elt("TR", fixed_horizontal_elements[i]);
if (parent_tr.className.match("tr_shaded")) {
fixed_horizontal_elements[i].style.backgroundColor = even_odd_color(scrolling_table_tr_counters[table_i]);
scrolling_table_tr_counters[table_i]++;
}
}
}
// Add event listeners.
for (i = 0; i < scrolling_table_div_ids.length; i++) {
scroll_div = document.getElementById(scrolling_table_div_ids[i]);
scroll_div.addEventListener("scroll", freeze_pane_listener(scroll_div, scrolling_table_div_ids[i]));
}
Posted 2016-08-28.