DataTables

HTML

<button id="drawButton">Draw table</button><br />
<div id="container"></div>

<link href="https://cdn.datatables.net/2.1.4/css/dataTables.dataTables.css" rel="stylesheet">
<link href="https://cdn.datatables.net/scroller/2.4.3/css/scroller.dataTables.min.css" rel="stylesheet">
<link href="https://cdn.datatables.net/fixedcolumns/5.0.3/css/fixedColumns.dataTables.min.css" rel="stylesheet">

<script src="https://cdn.datatables.net/2.1.4/js/dataTables.js" type="text/javascript"></script>
<script src="https://cdn.datatables.net/scroller/2.4.3/js/dataTables.scroller.js" type="text/javascript"></script>
<script src="https://cdn.datatables.net/fixedcolumns/5.0.3/js/dataTables.fixedColumns.min.js" type="text/javascript"></script>

CSS

#container {
  height: 2000px;
  
  td, th {
    padding: 2px 8px 1px 8px;
  }
}

JavaScript

const columnCount = 100;
const rowCount = 500;

$(document).ready(function (){
   const container = document.getElementById('container');
   const drawButton = document.getElementById('drawButton');
   
   // Create data
   const data = [];
   for (let i=0;i<rowCount;i++) {
     const rowData = [];
     for (let j=0;j<columnCount;j++) {
       rowData.push(`(${i},${j})`);
     }
     data.push(rowData);
   }
   
   drawButton.onclick = () => {
    container.innerHTML = '<table id="example" class="display" cellspacing="0" width="100%"> <thead> <tr></tr> </thead> </table>';
   
     const table = document.getElementById('example');
     const tableHeaderTr = table.querySelector('thead tr');
     const columns = [];
     for (let i=0;i<columnCount;i++) {
       // Add header cells
       const th = document.createElement('th');
       th.innerText = "h"+i;
       tableHeaderTr.append(th);
     }
     
     const time = new Date().getTime();
     dt = $(table).dataTable({
        ajax: (requestParams, callback, settings) => {
          var page = {
            data: data.slice(requestParams.start, requestParams.start + requestParams.length),
            draw: requestParams.draw,
            recordsTotal: data.length,
            recordsFiltered: data.length
          };
          callback(page);
        },
        layout: {
          // Show nothing else than the table, especially not datatables' own pagination controls
          topStart: null,
          topEnd: null,
          bottomStart: null,
          bottomEnd: null,
        },
        scroller: {     
          loadingIndicator: true,
          displayBuffer: 4, // Decrease buffer size to not draw too many rows at once (Default is 9)
        },
        serverSide: true,
        fixedColumns: { leftColumns: 1 },
        processing: true,
        autoWidth: false,
        searching: false,
        orderClasses: false, // Switch off sorting_1/sorting_2 css classes for sorted columns
        orderMulti: false, //...