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