A zero-dependency, virtual-scrolling table component with real-time search. Designed to handle datasets from dozens to millions of rows with consistent performance.
InfiniteTable renders only the rows visible in the viewport (plus a small buffer), recycling DOM nodes as you scroll. This keeps memory flat and scrolling smooth regardless of dataset size. A debounced search filter pre-computes lowercase strings per row so filtering 1M rows completes in under 100ms.
- Virtual scrolling — Fixed row height enables O(1) index calculation. Only ~60-80 DOM rows exist at any time, even with 500K+ data rows.
- Real-time search — Debounced full-text filtering across all visible columns. Results update as you type.
- Row selection — Single and multi-select modes. Click toggles selection in both modes. Single mode allows at most one selected row; multi mode allows any number.
:selectedfilter — Type:selectedin the search box to narrow the view to only your selected rows.- Column definitions — Optional
columnDefsmap overrides display titles (e.g.AssayType→ "Assay Type"). - Bootstrap modal wrapper —
createModalTablewraps InfiniteTable in a Bootstrap 5 modal with lazy data loading, a spinner, and an OK/Cancel footer. API-compatible with data-modal'sModalTable. - No dependencies — Pure ES6 modules. No jQuery, no build step required.
As a Git dependency in package.json:
{
"dependencies": {
"infinite-table": "github:igvteam/infinite-table"
}
}Then import from source:
import { createInfiniteTable, createModalTable } from 'infinite-table/src/index.js'Add the stylesheet to your HTML:
<link rel="stylesheet" href="node_modules/infinite-table/css/infinite-table.css">import { createInfiniteTable } from 'infinite-table/src/index.js'
const table = createInfiniteTable({
container: document.getElementById('my-container'),
columns: ['Name', 'Category', 'Value'],
columnDefs: { Category: { title: 'Track Category' } },
selectionStyle: 'multi' // or 'single'
})
table.setData([
{ Name: 'Alpha', Category: 'A', Value: '100' },
{ Name: 'Beta', Category: 'B', Value: '200' }
])
// Later:
table.getSelectedData() // returns array of selected row objects
table.getFilteredData() // returns rows matching current search
table.clearSelection()
table.scrollToTop()
table.destroy()import { createModalTable } from 'infinite-table/src/index.js'
const modalTable = createModalTable({
id: 'encode-modal',
title: 'ENCODE',
selectionStyle: 'multi',
datasource: myDataSource, // object with tableColumns() and tableData() async methods
okHandler: (selected) => {
console.log('User selected:', selected)
}
})
// Open the modal — table builds lazily on first show
modalTable.modal.show()
// Swap data source (clears and rebuilds on next show)
modalTable.setDatasource(newDataSource)
// Update modal content
modalTable.setTitle('New Title')
modalTable.setDescription('<em>Optional HTML description</em>')
// Tear down
modalTable.remove()The datasource object must implement:
async tableColumns()— returnsstring[]of column namesasync tableData()— returnsobject[]of row datacolumns(optional) —string[]used to build metadata on selected rowscolumnDefs(optional) —{ [column]: { title: string } }display title overridesrowHandler(optional) —(row) => objecttransforms selected rows before returning fromokHandler
Serve the project root with any static HTTP server and open the test pages in a browser.
npx serve .Then visit http://localhost:3000/test/<page>.html.
| Page | What it tests |
|---|---|
| basic.html | Renders 50 rows with 4 columns. Verify headers, cell content, and alternating row colors. |
| large-dataset.html | Generates 500K rows. Verify smooth scrolling and flat DOM node count (~60-80 nodes). Performance stats displayed at top. |
| search.html | 10K rows with real-time filtering. Type to filter, verify result count updates and scroll resets to top. |
| selection.html | Two tables side by side — multi-select and single-select. Test click-to-toggle behavior in both modes. |
| modal.html | Bootstrap 5 modal with simulated async data loading (500ms delay). Verify spinner, row selection, and OK handler output. |
| encode-like.html | Realistic ENCODE-style dataset with columnDefs title overrides and a rowHandler that transforms selections into {name, url, color, metadata} track config objects. |
MIT