JSFiddle - React, Tailwind, and code Playground

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

/*
가상 재화면 구성 과정은 아래와 같다:
1
--------
   -------2-
      --------
         --------
            --------
               --------
                  --------
                     --------
                        --------
                           --------
                              --------
                                 --------
                                    --------
                                       --------
                                          --------

==================================================

[=] - 실제 스크롤 가능 높이 (h)
[-] - "페이지";  모든 (n) 페이지의 총 높이는 (총 높이th) = (페이지 높이 ph) * (페이지 n)

페이지 사이 겹치는 부분 (cj) 과 스크롤바 길이
스크롤 높이에 따른 페이지 전환시점에 넘어갈 것이다.

부드럽게 유지하기 위해 (n) 과 (cj) 를 최소화해야 한다.
(ph) 를 (h) 값의 1/100 으로 설정해야 좋은 시발점이 된다.
*/


var th = 1000000000;            // 가상 높이
var h =  1000000;               // 실제 스크롤 가능 높이
var ph = h / 100;               // 페이지 높이
var n = Math.ceil(th / ph);     // 페이지 개수
var vp = 400;                   // 보이는 부분 높이
var rh = 50;                    // 행 높이
var cj = (th - h) / (n - 1);    // "넘어가는" 계수

var page = 0;                   // 현재 페이지
var offset=0;                   // 현재 페이지 위치
var prevScrollTop = 0;

var rows = {};                  // 캐시된 행 노드

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();
    
    // 다음 페이지
    if (scrollTop + offset > (page+1)*ph) {
        page++;
        offset = Math.round(page * cj);
        viewport.scrollTop(prevScrollTop = scrollTop - cj);
        removeAllRows();
    }
    // 이전 페이지
    else if (scrollTop...