NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Wikipedia sticky table headers // @namespace https://github.com/sullerandras/wikipedia-sticky-table-headers // @version 0.1 // @description Displays the table headers on the top of the screen while otherwise the table header would not be visible. Useful for tall tables with many columns. // @author Andras Suller // @match https://*.wikipedia.org/wiki/* // @grant unsafeWindow // ==/UserScript== (function() { 'use strict'; var bigTables = []; var newTables = []; /** Returns the first row where all cells have colSpan == 1 */ function getHeaderRowForTable(table) { var rows = table.querySelectorAll('tr'); for (var rowIndex = 0; rowIndex < rows.length; rowIndex++) { var row = rows[rowIndex]; var cells = row.children; var good = true; for (var colIndex = 0; colIndex < cells.length; colIndex++) { var cell = cells[colIndex]; if (cell.colSpan > 1) { good = false; break; } } if (good) { return row; } } return rows[0]; } function addHeaderToTable(table) { var headerRow = getHeaderRowForTable(table); var newTable = document.createElement('table'); table.parentNode.insertBefore(newTable, table); newTable.className = table.className.replace('collapsible', ''); newTable.style.position = 'fixed'; newTable.style.display = 'none'; newTable.style.top = '0'; newTable.style.margin = '0'; newTable.style['table-layout'] = 'fixed'; var newTr = document.createElement('tr'); newTable.appendChild(newTr); newTr.innerHTML = headerRow.innerHTML; setNewCellsWidth(headerRow, newTr); copyEventHandlers(headerRow, newTr); newTable.oldHeaderRow = headerRow; newTable.thresholdToShow = headerRow.offsetTop; return newTable; } function setNewCellsWidth(headerRow, newTr) { for (var i = 0; i < headerRow.children.length; i++) { var cell = headerRow.children[i]; var newCell = newTr.children[i]; newCell.style.width = cell.offsetWidth + 'px'; newCell.style['box-sizing'] = 'border-box'; } } function copyEventHandlers(headerRow, newTr) { var $ = unsafeWindow.jQuery; if (!$) { console.log('no jQuery!'); return; } for (var i = 0; i < headerRow.children.length; i++) { var cell = headerRow.children[i]; var newCell = newTr.children[i]; var events = $._data(cell, 'events'); if (events) { $.each(events, function() { // iterate registered handler of original $.each(this, function() { var handler = this.handler; $(newCell).bind(this.type, function(){ // the event handler function is called with "this" set to newCell, but it breaks // table sorts, so we call the event handler with "this" bound to cell as it would // be if the users clicks on the original table header. handler.apply(cell, arguments); }); }); }); } else { // console.log('no events for', cell, $.expando, cell[$.expando]); } } } function run() { var tables = document.getElementsByTagName('table'); for (var i = 0; i < tables.length; i++) { var table=tables[i]; if (table.offsetHeight >= window.innerHeight) { bigTables.push(table); } } for (i = 0; i < bigTables.length; i++) { newTables.push(addHeaderToTable(bigTables[i])); } unsafeWindow.bigTables = bigTables; unsafeWindow.newTables = newTables; setInterval(function(){ if (!document.body.onscroll) { console.log('adding onscroll to body'); document.body.onscroll = handleOnScroll; } }, 1000); handleOnScroll(); unsafeWindow.onresize = function() { console.log('onresize'); for (var i = 0; i < bigTables.length; i++) { var table = bigTables[i]; var newTable = newTables[i]; var headerRow = newTable.oldHeaderRow; var newTr = newTable.querySelectorAll('tr')[0]; setNewCellsWidth(headerRow, newTr); copyEventHandlers(headerRow, newTr); } handleOnScroll(); }; } function handleOnScroll() { for (var i = 0; i < bigTables.length; i++) { var table = bigTables[i]; var newTable = newTables[i]; var rect = table.getBoundingClientRect(); if (rect.top < 0 && -rect.top > newTable.thresholdToShow && -rect.top < rect.height) { newTable.style.display = 'table'; } else { newTable.style.display = 'none'; } newTable.style.left = rect.left + 'px'; newTable.style.width = table.offsetWidth + 'px'; } } setTimeout(run, 1000); })();