Move sticky headers to separate script

rel-1.0
Vitaliy Filippov 2016-08-11 17:43:55 +03:00
parent 3c02d4e2dd
commit 557a5a817b
2 changed files with 200 additions and 175 deletions

198
stickyheaders.js Normal file
View File

@ -0,0 +1,198 @@
// Simple Sticky Header and Column implementation for HTML tables
// (c) Vitaliy Filippov 2016
// License: MPL 2.0+
// USAGE:
// makeStickyHeaders(table): add sticky header and footer to table
// table.parentNode is assumed to be the scroll container
// fixStickyRow(table, tr): fix table row (tr) after modifications
// fixStickyHeader(table): fix table header after modifications
// WARNING 1: after using this plugin all columns and rows except 0th ones become shifted by 1.
// table.rows[1] and table.rows[*].cells[1] become special invisible rows/cells with copies of first
// row and column content inside.
// WARNING 2: this plugin does NOT support border-collapse because it is implemented by non-integer border
// sizes O_o and slightly differently in different browsers which leads to problems calculating cell positions.
// WARNING 3: all table cells should obviously have non-transparent background
// because fixed ones are positioned over normal.
var _scri = 0;
function makeStickyHeaders(table)
{
if (!table._scrstyle)
{
table._scrstyle = document.createElement('style');
(document.head||document.getElementsByTagName('head')[0]).appendChild(table._scrstyle);
table._scri = _scri++;
}
var rs = table.rows;
var w = [], l = [], h = rs[0].offsetHeight;
var sr = rs[0].cloneNode(true);
sr.insertBefore(document.createElement('td'), sr.firstChild);
sr.children[0].style.width = '0';
sr.children[0].style.display = 'block';
sr.children[0].style.position = 'absolute';
sr.children[0].style.padding = '0';
for (var i = 0; i < rs[0].children.length; i++)
{
var e = rs[0].children[i];
w[i] = e.offsetWidth;
l[i] = e.offsetLeft;
}
table._widths = w;
table._sizerow = sr;
rs[0].style.height = '0';
rs[0].style.display = 'block';
rs[0].style.position = 'absolute';
rs[0].parentNode.insertBefore(sr, rs[0].nextSibling);
for (var i = 0; i < rs[0].children.length; i++)
{
var e = rs[0].children[i];
e.style.position = 'absolute';
e.style.width = w[i]+'px';
e.style.height = h+'px';
e.style.left = l[i]+'px';
e.style.display = 'block';
e.style.zIndex = '1';
}
for (var i = rs.length-1; i >= 0; i--)
{
if (rs[i] == sr)
continue;
var e = rs[i].children[0];
var d = e.cloneNode(true);
d.style.visibility = 'hidden';
e.parentNode.insertBefore(d, e.nextSibling);
if (i != 0)
{
e.style.height = e.parentNode.offsetHeight+'px';
e.style.position = 'absolute';
e.style.display = 'block';
e.style.width = w[0]+'px';
e.style.zIndex = '1';
}
else
{
e.style.left = '';
e.style.zIndex = '2';
}
e.className += ' _scri'+table._scri;
}
table.parentNode.addEventListener('scroll', function(e)
{
var l = this.scrollLeft, t = this.scrollTop;
table.rows[0].style.top = t+'px';
table._scrstyle.innerHTML = '._scri'+table._scri+' { left: '+l+'px; }';
});
}
// handle non-header row change
function fixStickyRow(table, tr, nocols)
{
var e = tr.children[0];
if (e.className.indexOf(' _scri'+table._scri) >= 0)
{
// refix
tr.removeChild(tr.children[1]);
var d = e.cloneNode(true);
d.className = d.className.replace(' _scri'+table._scri, '');
d.style.visibility = 'hidden';
d.style.width = '';
d.style.height = '';
d.style.position = '';
d.style.display = '';
d.style.zIndex = '';
e.parentNode.insertBefore(d, e.nextSibling);
// fix size
e.style.height = tr.offsetHeight+'px';
e.style.width = d.offsetWidth+'px';
// force layouting
if (!nocols)
table.parentNode.scrollTop = table.parentNode.scrollTop;
}
else
{
// fix new
var d = e.cloneNode(true);
d.style.visibility = 'hidden';
e.parentNode.insertBefore(d, e.nextSibling);
e.style.height = e.parentNode.offsetHeight+'px';
e.style.width = e.offsetWidth+'px';
e.style.position = 'absolute';
e.style.display = 'block';
e.style.zIndex = '1';
e.className += ' _scri'+table._scri;
}
if (!nocols)
fixStickyColumnSizes(table);
}
function fixStickyColumnSizes(table)
{
// check&fix column sizes
var changed = false;
for (var i = 1; i < table._sizerow.children.length; i++)
{
var ne = table._sizerow.children[i];
var nw = ne.offsetWidth;
var cw = table._widths[i-1];
if (nw != cw)
{
table._widths[i-1] = nw;
changed = true;
if (i == 1)
{
// resize fixed column
for (var j = 0; j < table.rows.length; j++)
{
if (table.rows[j] != table._sizerow)
{
table.rows[j].children[0].style.width = nw+'px';
table.rows[j].children[0].style.height = (j == 0 ? table._sizerow.offsetHeight : table.rows[j].offsetHeight)+'px';
}
}
}
}
}
if (changed)
{
// reposition fixed header
for (var i = 1; i < table._sizerow.children.length; i++)
{
table.rows[0].children[i].style.left = table._sizerow.children[i].offsetLeft+'px';
table.rows[0].children[i].style.width = table._sizerow.children[i].offsetWidth+'px';
table.rows[0].children[i].style.height = table._sizerow.children[i].offsetHeight+'px';
}
}
}
// handle header row change
function fixStickyHeader(table, nocols)
{
var tr = table.rows[0];
var sr = table._sizerow;
while (sr.children[1])
sr.removeChild(sr.children[1]);
for (var i = 0; i < tr.children.length; i++)
{
if (i == 1)
continue;
var e = tr.children[i].cloneNode(true);
if (i == 0)
e.className = e.className.replace(' _scri'+table._scri, '');
e.style.position = '';
e.style.width = '';
e.style.height = '';
e.style.left = '';
e.style.display = '';
e.style.zIndex = '';
sr.appendChild(e);
}
// force layouting
table.parentNode.scrollTop = table.parentNode.scrollTop;
if (!nocols)
fixStickyColumnSizes(table);
}

View File

@ -4,10 +4,9 @@
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="stylesheet" type="text/css" href="treegrid.css" />
<link rel="stylesheet" type="text/css" href="../tplext/fht/defaultTheme.css">
<script type="text/javascript" src="../tplext/jquery-1.11.2.min.js"></script>
<script type="text/javascript" src="../tplext/fht/jquery.fixedheadertable.js"></script>
<script type="text/javascript" src="util.js"></script>
<script type="text/javascript" src="treegrid.js"></script>
<script type="text/javascript" src="stickyheaders.js"></script>
<style>
table, tr, td, th { box-sizing: border-box; background: white; }
table { border-collapse: separate; border-spacing: 0px; }
@ -19,178 +18,6 @@ th, td { border-width: 0 1px 1px 0 !important; }
<div class="scroller" id="scroller"></div>
<script>
<!--
var _scri = 0;
// handle non-header row change
function fixStickyRow(table, tr)
{
var e = tr.children[0];
if (e.className.indexOf(' _scri'+table._scri) >= 0)
{
// refix
tr.removeChild(tr.children[1]);
var d = e.cloneNode(true);
d.className = d.className.replace(' _scri'+table._scri, '');
d.style.visibility = 'hidden';
d.style.width = '';
d.style.height = '';
d.style.position = '';
d.style.display = '';
d.style.zIndex = '';
e.parentNode.insertBefore(d, e.nextSibling);
// fix size
e.style.height = tr.offsetHeight+'px';
e.style.width = d.offsetWidth+'px';
// force layouting
table.parentNode.scrollTop = table.parentNode.scrollTop;
}
else
{
// fix new
var d = e.cloneNode(true);
d.style.visibility = 'hidden';
e.parentNode.insertBefore(d, e.nextSibling);
e.style.height = e.parentNode.offsetHeight+'px';
e.style.width = e.offsetWidth+'px';
e.style.position = 'absolute';
e.style.display = 'block';
e.style.zIndex = '1';
e.className += ' _scri'+table._scri;
}
fixStickyColumnSizes(table);
}
function fixStickyColumnSizes(table)
{
// check&fix column sizes
var changed = false;
for (var i = 1; i < table._sizerow.children.length; i++)
{
var ne = table._sizerow.children[i];
var nw = ne.offsetWidth;
var cw = table._widths[i-1];
if (nw != cw)
{
table._widths[i-1] = nw;
changed = true;
if (i == 1)
{
// resize fixed column
for (var j = 0; j < table.rows.length; j++)
{
if (table.rows[j] != table._sizerow)
{
table.rows[j].children[0].style.width = nw+'px';
table.rows[j].children[0].style.height = (j == 0 ? table._sizerow.offsetHeight : table.rows[j].offsetHeight)+'px';
}
}
}
}
}
if (changed)
{
// reposition fixed header
for (var i = 1; i < table._sizerow.children.length; i++)
{
table.rows[0].children[i].style.left = table._sizerow.children[i].offsetLeft+'px';
table.rows[0].children[i].style.width = table._sizerow.children[i].offsetWidth+'px';
table.rows[0].children[i].style.height = table._sizerow.children[i].offsetHeight+'px';
}
}
}
// handle header row change
function fixStickyHeader(table)
{
var tr = table.rows[0];
var sr = table._sizerow;
while (sr.children[1])
sr.removeChild(sr.children[1]);
for (var i = 0; i < tr.children.length; i++)
{
if (i == 1)
continue;
var e = tr.children[i].cloneNode(true);
if (i == 0)
e.className = e.className.replace(' _scri'+table._scri, '');
e.style.position = '';
e.style.width = '';
e.style.height = '';
e.style.left = '';
e.style.display = '';
e.style.zIndex = '';
sr.appendChild(e);
}
// force layouting
table.parentNode.scrollTop = table.parentNode.scrollTop;
fixStickyColumnSizes(table);
}
function stickFirst(table)
{
if (!table._scrstyle)
{
table._scrstyle = document.createElement('style');
(document.head||document.getElementsByTagName('head')[0]).appendChild(table._scrstyle);
table._scri = _scri++;
}
var rs = table.rows;
var w = [], l = [], h = rs[0].offsetHeight;
var sr = rs[0].cloneNode(true);
// только не нужно border-collapse юзать, это ведёт к долгому пиксельхантингу в разных браузерах
sr.insertBefore(document.createElement('td'), sr.firstChild);
sr.children[0].style.width = '0';
sr.children[0].style.display = 'block';
sr.children[0].style.position = 'absolute';
sr.children[0].style.padding = '0';
for (var i = 0; i < rs[0].children.length; i++)
{
var e = rs[0].children[i];
w[i] = e.offsetWidth;
l[i] = e.offsetLeft;
}
table._widths = w;
table._sizerow = sr;
rs[0].style.height = '0';
rs[0].style.display = 'block';
rs[0].style.position = 'absolute';
rs[0].parentNode.insertBefore(sr, rs[0].nextSibling);
for (var i = 0; i < rs[0].children.length; i++)
{
var e = rs[0].children[i];
e.style.position = 'absolute';
e.style.width = w[i]+'px';
e.style.height = h+'px';
e.style.left = l[i]+'px';
e.style.display = 'block';
e.style.zIndex = '1';
}
for (var i = rs.length-1; i >= 0; i--)
{
if (rs[i] == sr)
continue;
var e = rs[i].children[0];
var d = e.cloneNode(true);
d.style.visibility = 'hidden';
e.parentNode.insertBefore(d, e.nextSibling);
if (i != 0)
{
e.style.height = e.parentNode.offsetHeight+'px';
e.style.position = 'absolute';
e.style.display = 'block';
e.style.width = w[0]+'px';
e.style.zIndex = '1';
}
else
{
e.style.left = '';
e.style.zIndex = '2';
}
e.className += ' _scri'+table._scri;
}
table.parentNode.addEventListener('scroll', function(e)
{
var l = this.scrollLeft, t = this.scrollTop;
rs[0].style.top = t+'px';
table._scrstyle.innerHTML = '._scri'+table._scri+' { left: '+l+'px; }';
});
}
onDomReady(function()
{
var i = {
@ -220,7 +47,7 @@ onDomReady(function()
} ]);
}, 1000);
setTimeout(function() {
stickFirst(TG.table);
makeStickyHeaders(TG.table);
}, 3000);
TG.onExpand = function(node)
{