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 +...