Infinite Scroll
Load data on demand with infinite scrolling and sliding window memory management.
ZenGrid’s infinite scrolling feature enables efficient handling of massive datasets by loading data on demand and managing memory with a sliding window approach.
Configuration
Enable infinite scrolling with optional sliding window:
const grid = new ZenGrid(container, { infiniteScrolling: { enabled: true, threshold: 10, // Load more when within 10 rows of bottom initialRowCount: 100, // Initial number of rows to display enableSlidingWindow: true, // Enable memory management windowSize: 500, // Keep 500 rows in memory pruneThreshold: 0.3, // Prune when 30% outside window onDataPruned: (stats) => { console.log('Pruned rows:', stats.prunedRows); } }});Infinite scrolling is ideal for datasets with thousands or millions of rows where loading all data at once would be impractical.
Data Loading Callback
Implement the onLoadMoreRows callback to fetch data:
const grid = new ZenGrid(container, { infiniteScrolling: { enabled: true, threshold: 10, initialRowCount: 100 }, onLoadMoreRows: async (currentRowCount) => { console.log('Current rows:', currentRowCount);
// Fetch next batch from server const response = await fetch( `/api/data?offset=${currentRowCount}&limit=100` );
const newRows = await response.json();
// Return the new rows return newRows; }});Callback Signature
type LoadMoreRowsCallback = (currentRowCount: number) => Promise<any[][]>;The callback receives the current row count, allowing you to calculate the correct offset for pagination.
Sliding Window Memory Management
For extremely large datasets, enable sliding window to limit memory usage:
const grid = new ZenGrid(container, { infiniteScrolling: { enabled: true, enableSlidingWindow: true, windowSize: 500, // Keep 500 rows in memory pruneThreshold: 0.3, // Prune when 30% of window is off-screen onDataPruned: (stats) => { console.log('Memory management:', { rowsInMemory: stats.rowsInMemory, totalRowsLoaded: stats.totalRowsLoaded, prunedRows: stats.prunedRows, virtualOffset: stats.virtualOffset }); } }});How Sliding Window Works
- As you scroll, new rows are loaded and added to memory
- When memory exceeds
windowSize, old rows outside the viewport are pruned - The grid maintains a virtual offset to handle scrollbar position correctly
- Previously pruned data is re-fetched if you scroll back to it
With sliding window enabled, scrolling back to previously viewed data will trigger new data fetches. Cache results on the server side if needed.
Memory Stats
Query memory usage statistics:
const stats = grid.getSlidingWindowStats();console.log(stats);// {// virtualOffset: 2000, // Virtual row offset for pruned rows// rowsInMemory: 500, // Current rows in DOM// totalRowsLoaded: 2500, // Total rows loaded so far// prunedRows: 2000 // Total rows pruned// }Threshold Configuration
The threshold parameter controls when to load more data:
// Load when within 10 rows of bottominfiniteScrolling: { enabled: true, threshold: 10}
// Load when within 50 rows of bottom (more aggressive)infiniteScrolling: { enabled: true, threshold: 50}
// Load only when at the very bottominfiniteScrolling: { enabled: true, threshold: 0}A higher threshold provides a smoother experience by preloading data before users reach the bottom.
Reset Infinite Scrolling
Reset the infinite scroll state:
// Reset infinite scrolling stategrid.resetInfiniteScrolling();
// This will:// - Clear all loaded data// - Reset row count to initialRowCount// - Clear sliding window stats// - Trigger initial data loadExample: Backend Integration
Complete example with backend API:
const grid = new ZenGrid(container, { columns: [ { id: 'id', field: 'id', header: 'ID', width: 80 }, { id: 'name', field: 'name', header: 'Name', width: 200 }, { id: 'email', field: 'email', header: 'Email', width: 250 } ], infiniteScrolling: { enabled: true, threshold: 20, initialRowCount: 100, enableSlidingWindow: true, windowSize: 1000, pruneThreshold: 0.2 }, onLoadMoreRows: async (currentRowCount) => { try { const response = await fetch( `/api/users?offset=${currentRowCount}&limit=100` );
if (!response.ok) { throw new Error('Failed to load data'); }
const result = await response.json();
// Transform API response to grid format return result.users.map(user => [ user.id, user.name, user.email ]); } catch (error) { console.error('Error loading data:', error); return []; // Return empty array on error } }});Example: Infinite Scroll with Search
Combine infinite scrolling with server-side search:
let currentSearchQuery = '';
const grid = new ZenGrid(container, { infiniteScrolling: { enabled: true, threshold: 15, initialRowCount: 50 }, onLoadMoreRows: async (currentRowCount) => { const response = await fetch('/api/search', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query: currentSearchQuery, offset: currentRowCount, limit: 50 }) });
const data = await response.json(); return data.results; }});
// Search input handlerdocument.getElementById('search-input').addEventListener('input', (e) => { currentSearchQuery = e.target.value;
// Reset and reload with new search grid.resetInfiniteScrolling();});Memory Management Tips
Optimize Window Size
Choose appropriate window size based on row height and viewport:
const viewportHeight = window.innerHeight;const rowHeight = 40; // pixelsconst rowsInViewport = Math.ceil(viewportHeight / rowHeight);
// Keep 10x viewport rows in memoryconst windowSize = rowsInViewport * 10;
infiniteScrolling: { enabled: true, enableSlidingWindow: true, windowSize: windowSize}Monitor Memory Usage
Track memory consumption:
setInterval(() => { const stats = grid.getSlidingWindowStats(); const estimatedMemory = stats.rowsInMemory * 100; // ~100 bytes per row
console.log(`Memory usage: ~${(estimatedMemory / 1024).toFixed(2)} KB`);
if (stats.rowsInMemory > 2000) { console.warn('High memory usage detected'); }}, 5000);Loading Indicator
Show a loading indicator while fetching data:
const grid = new ZenGrid(container, { infiniteScrolling: { enabled: true, threshold: 10 }, onLoadMoreRows: async (currentRowCount) => { // Show loading indicator showLoadingSpinner();
try { const data = await fetchData(currentRowCount); return data; } finally { // Hide loading indicator hideLoadingSpinner(); } }});Always provide visual feedback during data loading to improve user experience.
Performance Considerations
- Window Size: Balance memory usage with scroll performance. Larger windows mean fewer reloads but more memory usage.
- Prune Threshold: Lower values (0.1-0.3) prune more aggressively, saving memory but causing more reloads.
- Load Threshold: Higher values (20-50 rows) provide smoother scrolling by preloading data earlier.
- Server Response Time: Optimize your backend API for quick responses. Consider caching and database indexing.
// High-performance configurationinfiniteScrolling: { enabled: true, threshold: 30, // Preload early enableSlidingWindow: true, windowSize: 1000, // Large window for fewer reloads pruneThreshold: 0.2 // Moderate pruning}
// Memory-optimized configurationinfiniteScrolling: { enabled: true, threshold: 10, // Load just in time enableSlidingWindow: true, windowSize: 300, // Small window to save memory pruneThreshold: 0.1 // Aggressive pruning}