JavaScript List Virtualization Example

More info: https://ykamal.com/posts/2024-07-05-javascript-performance-tip-list-virtualization

by ykamal

HTML

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <p>Scorll down inside the list. <a target="_blank" href="https://ykamal.com/posts/2024-07-05-javascript-performance-tip-list-virtualization">Read More</a></p>
  <div id="list-container"></div>
</body>
</html>

JavaScript

function createVirtualizedList(container, itemHeight, totalItems, renderItem) {
  const visibleItems = Math.ceil(container.clientHeight / itemHeight) + 1;
  const listHeight = totalItems * itemHeight;
  let scrollTop = 0;

  function setupContainer() {
    container.style.overflow = 'auto';
    container.style.position = 'relative';
    const content = document.createElement('div');
    content.style.height = `${listHeight}px`;
    content.style.position = 'relative';
    container.appendChild(content);
    return content;
  }

  function render(content) {
    const startIndex = Math.floor(scrollTop / itemHeight);
    const endIndex = Math.min(startIndex + visibleItems, totalItems);

    content.innerHTML = '';
    const fragment = document.createDocumentFragment();
    for (let i = startIndex; i < endIndex; i++) {
      const item = renderItem(i);
      item.style.position = 'absolute';
      item.style.top = `${i * itemHeight}px`;
      item.style.left = '0';
      item.style.right = '0';
      item.style.height = `${itemHeight}px`;
      fragment.appendChild(item);
    }
    content.appendChild(fragment);
  }

  function handleScroll(content, event) {
    const newScrollTop = event.target.scrollTop;
    if (Math.abs(newScrollTop - scrollTop) > itemHeight / 2) {
      scrollTop = newScrollTop;
      requestAnimationFrame(() => render(content));
    }
  }

  const content = setupContainer();
  render(content);
  container.addEventListener('scroll', (event) => handleScroll(content, event));
}

// Usage
const container = document.getElementById('list-container');
container.style.height = '400px';
container.style.width = '300px';
container.style.border = '1px solid black';

const itemHeight = 50;
const totalItems = 10000;

createVirtualizedList(
  container,
  itemHeight,
  totalItems,
  (index) => {
    const item = document.createElement('div');
    item.textContent = `Item ${index + 1}`;
    item.style.border = '1px solid #ccc';
    item.style.boxSizing =...