Keyboard Navigation
useVirtualKeyboardNav() — attaches keydown listeners and manages a focused index. Works with any virtual list component.
ts
function useVirtualKeyboardNav(options: UseVirtualKeyboardNavOptions): UseVirtualKeyboardNavReturnOptions
itemCount
Ref<number> | number
Total item count.
scrollTo
(index, align?) => void
Called to scroll the list when focus moves.
target
Ref<HTMLElement | null> | HTMLElement · default: document
Element that receives keyboard events.
onActivate
(index: number) => void
Called on Enter or Space.
onChange
(index: number) => void
Called when the focused index changes.
loop
boolean · default: false
Whether to wrap around at boundaries.
Return value
focusedIndex
Readonly<Ref<number>>
Currently focused index, -1 if nothing focused.
setFocus
(index: number) => void
Programmatically set focus.
isFocused
(index: number) => boolean
Whether index is focused.
Keys handled
| Key | Action |
|---|---|
↑ | Move focus up |
↓ | Move focus down |
Home | Focus first item |
End | Focus last item |
PageUp | Jump −10 items |
PageDown | Jump +10 items |
Enter / Space | Call onActivate |
Example
vue
<script setup lang="ts">
import { computed, ref } from 'vue'
import { VirtualList, useVirtualKeyboardNav } from 'vue-virtual-scroller-kit'
import type { VirtualListExpose } from 'vue-virtual-scroller-kit'
const items = ref(Array.from({ length: 1000 }, (_, i) => ({ id: i, label: `Item ${i + 1}` })))
const listRef = ref<VirtualListExpose | null>(null)
const { focusedIndex, setFocus, isFocused } = useVirtualKeyboardNav({
itemCount: computed(() => items.value.length),
scrollTo: (i, align) => listRef.value?.scrollTo(i, align),
onActivate: (i) => console.log('Activated', items.value[i]),
loop: false,
})
</script>
<template>
<div tabindex="0" style="outline: none; border: 1px solid #ccc">
<VirtualList ref="listRef" :items="items" :estimated-item-size="48" style="height: 400px">
<template #default="{ item, index }">
<div
:class="{ focused: isFocused(index) }"
:aria-selected="isFocused(index)"
role="option"
@click="setFocus(index)"
>
{{ item.label }}
</div>
</template>
</VirtualList>
</div>
</template>