JSFiddle - React, Tailwind, and code Playground
by Jeado
HTML
<div id="viewport">
<div id="content"></div>
</div>
<div id="debug"></div>
CSS
#debug {
margin-left: 400px;
width: 200px;
background: beige;
font-size: 9pt;
opacity: 0.5;
border: 1px solid gray;
padding: 10px;
}
#viewport {
float: left;
border: 1px solid black;
overflow: auto;
width: 350px;
}
#content {
position: relative;
overflow: hidden;
}
.row {
position: absolute;
left: 0px;
width: 100%;
border-bottom: 1px dotted blue;
font-size: 9pt;
}
.row:hover {
background: rgba(0,0,255,0.05);
}
JavaScript
var th = 1000000000; // virtual height
var h = 1000000; // real scrollable height
var ph = h / 100; // page height
var n = Math.ceil(th / ph); // number of pages
var vp = 400; // viewport height
var rh = 50; // row height
var cj = (th - h) / (n - 1); // "jumpiness" coefficient
var page = 0; // current page
var offset=0; // current page offset
var prevScrollTop = 0;
var rows = {}; // cached row nodes
var viewport, content;
$(function() {
viewport = $("#viewport");
content = $("#content");
viewport.css("height",vp);
content.css("height",h);
viewport.scroll(onScroll);
viewport.trigger("scroll");
});
function onScroll() {
var scrollTop = viewport.scrollTop();
if (Math.abs(scrollTop-prevScrollTop) > vp)
onJump();
else
onNearScroll();
renderViewport();
logDebugInfo();
}
function onNearScroll() {
var scrollTop = viewport.scrollTop();
// next page
if (scrollTop + offset > (page+1)*ph) {
page++;
offset = Math.round(page * cj);
viewport.scrollTop(prevScrollTop = scrollTop - cj);
removeAllRows();
}
// prev page
else if (scrollTop + offset < page*ph) {
page--;
offset = Math.round(page * cj);
viewport.scrollTop(prevScrollTop = scrollTop + cj);
removeAllRows();
}
else
prevScrollTop = scrollTop;
}
function onJump() {
var scrollTop = viewport.scrollTop();
page = Math.floor(scrollTop * ((th-vp) / (h-vp)) * (1/ph));
offset = Math.round(page * cj);
prevScrollTop = scrollTop;
removeAllRows();
}
function removeAllRows() {
for (var i in rows) {
rows[i].remove();
delete rows[i];
}
}
function renderViewport() {
// calculate the viewport + buffer
var y = viewport.scrollTop() + offset,
...