// When scrolling, this script "freezes" the first row and all first table cells in the other rows
// of all tables who are wrapped in a div with class name "cross-reference-table".
// This way you keep a good reference to all the table data when your table becomes too big
// to fit the page and scrolling is needed.
// Table representation:
// C|HHHHHHHHHHHHHHHHH
// -------------------
// V|OOOOOOOOOOOOOOOOO
// V|OOOOOOOOOOOOOOOOO
// V|OOOOOOOOOOOOOOOOO
// V|OOOOOOOOOOOOOOOOO
// V|OOOOOOOOOOOOOOOOO
// The "H"s are tableHeader elements and will only scroll (H)orizontal.
// The "V"s are firstColumn elements and will only scroll (V)ertical.
// The "C" is the topLeft element (the (C)ornerstone) and stays fixed.
// The "O"s are all (O)rdinary td elements and will scroll in all directions.
// The script depends on a HTML table structure like so:
//
//
//
// | | | ...
//
//
// | | | | | | ...
//
//
// | | | | | | ...
//
// ...
//
//
// It is recommended that you also set an ID on the wrapper div to have more individual
// control over styling because you can have multiple "div.cross-reference-table" elements on your
// page(s).
// If the HTML of a table is already loaded into the DOM when document is ready, we can immediately invoke
// the prepareScrollingTable() function and start adding class names to the "C", "H" and "V" elements.
prepareScrollingTable();
// However, if the table HTML is injected dynamically via another (java)script "X", we need to
// invoke the prepareScrollingTable() function in script "X" after "X" appended its HTML to the DOM.
// We set the class names, needed in the repositionFreezeElements() function, dynamically
// in the prepareScrollingTable() function to avoid DRY, typo's or a cluttered HTML document.
function prepareScrollingTable() {
var scrolling_table_divs = document.getElementsByClassName("cross-reference-table");
[].forEach.call(scrolling_table_divs, function(table){
var headers = table.getElementsByTagName("TH");
for(var y = 0; y < headers.length; y++) {
if (y == 0) { // "C" element.
headers[y].className = "freeze";
} else { // "H" elements.
headers[y].className = "freeze_vertical";
}
}
var rows = table.getElementsByTagName("TR");
for(var y = 0; y < rows.length; y++) {
if (y > 0) { // not first row (containing the "C" and "H" elements).
var firstCell = rows[y].firstElementChild; // "V" elements.
firstCell.className += "freeze_horizontal";
}
}
table.addEventListener("scroll", repositionFreezeElements(table));
});
};
function repositionFreezeElements(divWrapper) {
// Wrapping a function so that the listener can be defined
return function() {
var translate_y = "translate(0," + divWrapper.scrollTop + "px)";
var translate_x = "translate(" + divWrapper.scrollLeft + "px,0px)";
var translate_xy = "translate(" + divWrapper.scrollLeft + "px," + divWrapper.scrollTop + "px)";
var tableHeaderElements = divWrapper.getElementsByClassName("freeze_vertical");
var firstColumnElements = divWrapper.getElementsByClassName("freeze_horizontal");
var topLeftElement = divWrapper.getElementsByClassName("freeze");
// The webkitTransform part is for a set of (very) old browsers
[].forEach.call(firstColumnElements, function(element) {
element.style.webkitTransform = translate_x;
element.style.transform = translate_x;
});
[].forEach.call(tableHeaderElements, function(element) {
element.style.webkitTransform = translate_y;
element.style.transform = translate_y;
});
[].forEach.call(topLeftElement, function(element) {
element.style.webkitTransform = translate_xy;
element.style.transform = translate_xy;
});
}
};