Skip to content

Select/Combobox: Virtual list keyboard navigation resets focus to first item when new DOM nodes mount #1696

@atebeta

Description

@atebeta

Describe the bug

When using Bits UI Combobox/Select components with virtual scrolling libraries (like virtua), keyboard navigation breaks because the focus automatically resets to the first item whenever new virtual list items are mounted to the DOM.

Expected Behavior

Arrow key navigation should maintain the current highlighted position when scrolling through a virtual list, allowing users to navigate through large datasets smoothly.

Actual Behavior

When navigating with arrow keys in a virtual list:

  1. User navigates to the last visible item (ie. 6) using arrow keys
  2. Virtual list adds new DOM nodes for items 7-10
  3. Focus automatically jumps back to item 0/1 (first item)
  4. User loses their navigation position

Root Cause

The issue occurs in SelectItemState constructor where onMountEffect and onDestroyEffect both call setHighlightedToFirstCandidate({ debounced: true }) whenever any item mounts or unmounts. In virtual lists, items are constantly mounting/unmounting as the user scrolls, causing unwanted focus resets.

Problematic code:

onMountEffect(() => {
    this.root.setHighlightedToFirstCandidate({ debounced: true });
});
onDestroyEffect(() => {
    this.root.setHighlightedToFirstCandidate({ debounced: true });
`});

Proposed Solution

I was able to fix the problem by adding a 'vList' property on the content element and checking for this before calling first candidate function.

Add conditional logic to detect virtual list contexts and skip the focus reset:

onMountEffect(() => {
    // Skip highlight reset for virtual lists
    if (this.root.contentNode?.hasAttribute('vList')) return;
    this.root.setHighlightedToFirstCandidate({ debounced: true });
});
onDestroyEffect(() => {
    // Skip highlight reset for virtual lists  
    if (this.root.contentNode?.hasAttribute('vList')) return;
    this.root.setHighlightedToFirstCandidate({ debounced: true });
});

Reproduction

https://stackblitz.com/edit/github-zt1racgs?file=src%2Froutes%2F%2Bpage.svelte

Logs

System Info

System:
    OS: macOS 15.0.1
    CPU: (12) arm64 Apple M2 Max
    Memory: 88.30 MB / 32.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 24.4.1 - ~/.nvm/versions/node/v24.4.1/bin/node
    npm: 11.4.2 - ~/.nvm/versions/node/v24.4.1/bin/npm
    pnpm: 8.12.1 - ~/Library/pnpm/pnpm
  Browsers:
    Brave Browser: 138.1.80.122
    Chrome: 138.0.7204.184
    Safari: 18.0.1
  npmPackages:
    @sveltejs/kit: ^2.12.0 => 2.26.0 
    bits-ui: ^2.8.0 => 2.8.11 
    svelte: ^5.20.2 => 5.36.16

Severity

annoyance

Metadata

Metadata

Assignees

No one assigned

    Labels

    triageA maintainer needs to review this issue and label it appropriately

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions