JSFiddle - React, Tailwind, and code Playground
HTML
<html>
<body onload='initialize()'>
<div id='header' class='row'>
<div class='title'>Title</div><div class='desc'>Description</div>
</div>
<div id='table'>
<!-- This is the layout for the rows, the mustache expressions are filled in with corresponding fields from "data" -->
<div class="Template">
<div class='title'>{{title}}</div><div class='desc'>{{description}}</div>
</div>
</div>
</body>
</html>
CSS
#table {
position: absolute;
top: 30px;
bottom: 0;
left: 10px;
right: 10px;
}
#header {
position: absolute;
top: 0;
height: 25px;
left: 10px;
right: 10px;
font-weight: bold;
font-size: 20px;
border-bottom: solid 1px #444;
}
.title {
width: 100px;
}
.desc {
position: absolute;
left: 120px;
top: 0px;
font-family: italic;
color: #444;
}
JavaScript
window.ui = window.ui || {};
/** @constructor */
ui.ListView = function(element, collection) {
this.el = element;
this.cellHeight = 30;
this.collection = collection;
// Find the first element with class 'Template' and use as a row template
var tt = this.el.getElementsByClassName('Template');
if (tt.length > 0) {
var t = tt[0];
this.template = t;
t.parentNode.removeChild(t);
}
this.content = document.createElement('div');
this.el.style.overflow = 'auto';
this.el.appendChild(this.content);
this.cells = [];
this.listeners = [];
// Juggle rows on scroll
this.el.addEventListener('scroll', this.redraw.bind(this));
// Ensure we have enough rows.
window.addEventListener('resize', this.redraw.bind(this));
// Get us started
this.redraw();
};
/** Ensure we have enough physical lines to fill the screen */
ui.ListView.prototype.ensure = function(desired) {
while (this.cells.length < desired) {
var cell = new ui.Template(this, this.template);
cell.element.style.position = 'absolute';
cell.element.style.y = 0;
cell.element.style.x = 0;
cell.element.style.height = this.cellHeight + 'px';
cell.element.style.width = '100%';
this.content.appendChild(cell.element);
this.cells.push(cell);
}
};
/** Rerender the rows */
ui.ListView.prototype.redraw = function() {
var cellh = this.cellHeight;
var oheight = this.el.offsetHeight;
var top = this.el.scrollTop;
var topRow = ~~(top / cellh);
var len = this.collection.length;
this.content.style.height = (len * cellh) + 'px';
var slosh = 10;
var desiredRows = ~~(oheight / cellh + slosh);
this.ensure(desiredRows);
var start = Math.max(topRow - slosh / 2, 0);
var end = Math.min(len, start + desiredRows);
start = Math.max(0, end - desiredRows);
var rows = [];
for (var i = start; i < start +...