Events #
Type-safe event system for vlist — subscribe to scroll, interaction, data, and lifecycle events.
Subscribing #
Use on to subscribe and the returned function to unsubscribe:
const unsub = list.on('item:click', ({ item, index, event }) => {
console.log('clicked', item)
})
// Later
unsub()
Or use off with the handler reference:
const handler = ({ item }) => console.log(item)
list.on('item:click', handler)
list.off('item:click', handler)
For one-time events, use the emitter's once method (available on the internal emitter, not the public API):
emitter.once('load:end', ({ items }) => {
console.log('First load complete:', items.length)
})
Interaction Events #
item:click #
Fired when an item is clicked.
list.on('item:click', ({ item, index, event }) => {
console.log(`Clicked item ${index}:`, item)
})
| Field | Type | Description |
|---|---|---|
item |
T |
The clicked item. |
index |
number |
Item index in the list. |
event |
MouseEvent |
The original DOM mouse event. |
item:dblclick #
Fired when an item is double-clicked.
list.on('item:dblclick', ({ item, index, event }) => {
openEditor(item)
})
| Field | Type | Description |
|---|---|---|
item |
T |
The double-clicked item. |
index |
number |
Item index in the list. |
event |
MouseEvent |
The original DOM mouse event. |
selection:change #
Fired when the selection changes. Only emitted when withSelection is active.
list.on('selection:change', ({ selected, items }) => {
console.log(`${selected.length} items selected`)
})
| Field | Type | Description |
|---|---|---|
selected |
Array<string | number> |
IDs of currently selected items. |
items |
T[] |
The selected item objects. |
Scroll Events #
scroll #
Fired on every scroll position change.
list.on('scroll', ({ scrollPosition, direction }) => {
console.log(`Scrolled ${direction} to ${scrollPosition}px`)
})
| Field | Type | Description |
|---|---|---|
scrollPosition |
number |
Current scroll offset along the main axis in pixels. |
direction |
'up' | 'down' |
Scroll direction. |
velocity:change #
Fired when the scroll velocity is updated. Emitted on every scroll frame after the builder's velocity tracker processes the new position.
list.on('velocity:change', ({ velocity, reliable }) => {
if (reliable && velocity > 5) {
console.log('Fast scrolling — hiding heavy UI')
}
})
| Field | Type | Description |
|---|---|---|
velocity |
number |
Absolute scroll velocity in px/ms. |
reliable |
boolean |
true when enough samples have accumulated (sampleCount >= MIN_RELIABLE_SAMPLES). false during the first few frames after idle or a stale gap reset. |
The reliable flag prevents false positives — after the velocity tracker resets (stale gap > 100ms or idle), the first frames produce near-zero velocity from small deltas. Wait for reliable: true before making loading or UI decisions based on velocity.
range:change #
Fired when the visible item range changes.
list.on('range:change', ({ range }) => {
console.log(`Visible: ${range.start}–${range.end}`)
})
| Field | Type | Description |
|---|---|---|
range |
Range |
The new visible range ({ start, end }). |
Data Events #
load:start #
Fired when an async data load begins. Only emitted when withAsync is active.
list.on('load:start', ({ offset, limit }) => {
console.log(`Loading ${limit} items from offset ${offset}`)
})
| Field | Type | Description |
|---|---|---|
offset |
number |
Starting offset of the request. |
limit |
number |
Number of items requested. |
load:end #
Fired when an async data load completes.
list.on('load:end', ({ items, total, offset }) => {
console.log(`Loaded ${items.length} items, total: ${total}`)
})
| Field | Type | Description |
|---|---|---|
items |
T[] |
The loaded items. |
total |
number |
Total item count (if reported by the adapter). |
offset |
number |
Starting offset of the completed request. |
error #
Fired when an error occurs during data loading or event handling.
list.on('error', ({ error, context }) => {
console.error(`Error in ${context}:`, error.message)
})
| Field | Type | Description |
|---|---|---|
error |
Error |
The error object. |
context |
string |
Where the error occurred (e.g. 'loadMore', 'adapter.read'). |
Lifecycle Events #
resize #
Fired when the list container is resized (detected via ResizeObserver).
list.on('resize', ({ height, width }) => {
console.log(`Container resized to ${width}×${height}`)
})
| Field | Type | Description |
|---|---|---|
height |
number |
New container height in pixels. |
width |
number |
New container width in pixels. |
This event fires regardless of scroll orientation. Both dimensions are always provided.
Complete Event Map #
interface VListEvents<T extends VListItem = VListItem> {
'item:click': { item: T; index: number; event: MouseEvent }
'item:dblclick': { item: T; index: number; event: MouseEvent }
'selection:change': { selected: Array<string | number>; items: T[] }
'scroll': { scrollPosition: number; direction: 'up' | 'down' }
'velocity:change': { velocity: number; reliable: boolean }
'range:change': { range: Range }
'load:start': { offset: number; limit: number }
'load:end': { items: T[]; total?: number; offset?: number }
'error': { error: Error; context: string }
'resize': { height: number; width: number }
}
Emitter Implementation #
Error Isolation #
Event handlers are wrapped in try-catch to prevent one handler from breaking others:
listeners[event]?.forEach((handler) => {
try {
handler(payload);
} catch (error) {
console.error(`[vlist] Error in event handler for "${event}":`, error);
}
});
Memory Management #
Listeners are stored in Sets for O(1) add/remove. The on() method returns an unsubscribe function, enabling clean cleanup:
const subscriptions: Unsubscribe[] = []
subscriptions.push(list.on('scroll', handleScroll))
subscriptions.push(list.on('selection:change', handleSelection))
// Cleanup
subscriptions.forEach(unsub => unsub())
createEmitter #
The low-level emitter factory, exported for feature authors:
function createEmitter<T extends EventMap>(): Emitter<T>
Returns an object with on, off, emit, once, clear, and listenerCount methods. See Types for the full interface.
Related #
- Types —
VListEvents,EventHandler,Unsubscribe - API Reference —
onandoffmethod signatures - Constants —
VELOCITY_SAMPLE_COUNT,MIN_RELIABLE_SAMPLES,STALE_GAP_MS - Exports —
createEmitterfor feature authoring
The event system provides clean decoupling between vlist internals and consumer code.