Keyboard Navigation
Problem
Users should be able to move focus through list items with the arrow keys. The virtualizer must scroll only when the focused item is outside the visible area — not on every keypress.
Solution
Track a focused index and use scrollToIndex with align: 'auto' so only out-of-view items trigger a scroll.
ts
import { createVirtualizer } from '@vielzeug/virtualit';
const rows = Array.from({ length: 1_000 }, (_, i) => `Row ${i}`);
let focusedIndex = 0;
function paint(items = virt.items, totalSize = virt.totalSize) {
listEl.style.height = `${totalSize}px`;
listEl.innerHTML = '';
for (const item of items) {
const el = document.createElement('div');
el.style.cssText = `position:absolute;top:${item.start}px;left:0;right:0;height:36px;`;
el.style.background = item.index === focusedIndex ? 'var(--highlight)' : '';
el.textContent = rows[item.index];
listEl.appendChild(el);
}
}
const virt = createVirtualizer(scrollEl, {
count: rows.length,
estimateSize: 36,
onChange: paint,
});
scrollEl.addEventListener('keydown', (e) => {
if (e.key === 'ArrowDown') {
focusedIndex = Math.min(focusedIndex + 1, rows.length - 1);
virt.scrollToIndex(focusedIndex, { align: 'auto' });
paint(); // update highlight even if visible range did not change
} else if (e.key === 'ArrowUp') {
focusedIndex = Math.max(focusedIndex - 1, 0);
virt.scrollToIndex(focusedIndex, { align: 'auto' });
paint();
}
});Pitfalls
scrollToIndexwithalign: 'auto'only scrolls when the item is outside the visible area. On first render, the virtualizer may not have rendered the focused item yet, making the initial scroll a no-op.- Arrow key events only fire if the scroll container or a child has focus. If focus is on the document body, key events do not reach the container's listener. Ensure the container has
tabindex="0". - Removing
tabindexfrom the scroll container or its items breaks keyboard focus. The container must remain focusable for key events to fire.