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