Missing `select` event on hidden documents · Issue #1253 · testing-library/user-event · GitHub
Skip to content

Missing select event on hidden documents #1253

Open

Description

@ph-fritsche

Reproduction example

image

Prerequisites

  1. Render a form control with selection range (<input type="text">, <textarea>)
  2. Change selection in the form control

Expected behavior

A select event is dispatched.

Actual behavior

There is no select event if document.visibilityState is hidden.

User-event version

14.5.2

Environment

DOM implementation: Chromium 131.0.6778.204 built on Debian GNU/Linux 12 (bookworm)

Additional context

similar to #553

Activity

  1. FrancoKaddour commented on Oct 23, 2026

    @FrancoKaddour

    Looked into this — happy to put up a PR. The chain today is: the select event is dispatched by the browser as a side effect of the native selection call (setUISelection → native setSelectionRange, via the interceptor in src/document/interceptor.ts). Like focus/blur, the browser suppresses it when document.visibilityState === 'hidden', and there's no simulated equivalent — patchFocus.ts handles focus/blur under a hidden document, but nothing covers select.

    So the fix mirrors patchFocus: when the document is hidden, dispatch select ourselves after the selection actually changes. Natural hook point is the setSelectionRange interceptor's then callback (interceptor.ts:~162), which is exactly the path the repro's direct element.setSelectionRange(1, 2) goes through. One prerequisite: select isn't in src/event/eventMap.ts, so dispatchDOMEvent(el, 'select') needs an entry added first (EventType: 'Event', bubbles: true, cancelable: false).

    One design question before I code it: the existing test "events are not dispatched on same microtask in browser" documents that the real browser fires select asynchronously. Should the simulated dispatch match that (defer to a microtask), or is a synchronous dispatch in the then acceptable here? Happy to go either way — just want to match the behavior you'd expect.

    Scope-wise I'd keep the first PR to setSelectionRange (the repro), and leave select() / setRangeText as a follow-up unless you'd rather cover them together.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions