Provide a general summary of the issue here
In Firefox, typing into a regular <input> that is a sibling of a DateField/DatePicker can move focus into one of the date segments mid-keystroke. Subsequent characters are then captured by that segment (e.g. typed digits land in the year segment) instead of the input the user was actually typing in.
🤔 Expected Behavior?
Typing in an unrelated input never moves focus into a date segment. The date field only reacts to input while one of its own segments is focused.
😯 Current Behavior
Focus jumps from the input into a date segment (commonly the last/year segment), and the keystrokes are consumed there.
Root cause is the document-level selectionchange listener in useDateSegment (packages/react-aria/src/datepicker/useDateSegment.ts):
useEvent(documentRef, 'selectionchange', () => {
let selection = window.getSelection();
if (selection?.anchorNode && nodeContains(ref.current, selection?.anchorNode)) {
selection.collapse(ref.current);
}
});
Two Firefox-specific behaviors combine:
- In Firefox the caret/selection inside an
<input>/<textarea> is not reflected in the document window.getSelection(); the document selection stays wherever it last was in regular DOM content — which, after the segments render/are interacted with, is inside a date segment. So nodeContains(ref.current, selection.anchorNode) is still true even though focus is in the input.
- Typing in the input still fires
selectionchange, so the handler runs and calls selection.collapse(ref.current). In Firefox, collapsing the selection onto a contentEditable node moves focus to it (Chromium does not).
Result: focus is pulled into the segment while the user types elsewhere. The handler currently re-collapses the selection regardless of whether the segment is the active element — but its purpose (per the existing comment) is only to keep the selection collapsed within a focused segment during Android-Chrome composition.
Note: user-select: none on the segment container (the workaround suggested for the click-focus issues #3163 / #3164) does not help here, because the focus move comes from a programmatic Selection.collapse(), not a user selection.
💁 Possible Solution
Only re-collapse when the segment is actually the active element:
if (
selection?.anchorNode &&
nodeContains(ref.current, selection?.anchorNode) &&
getActiveElement() === ref.current
) {
selection.collapse(ref.current);
}
This preserves the Android-Chrome composition fix (which only matters while the segment is focused) and eliminates the cross-element focus steal. Happy to open a PR (with a browser regression test) — see below.
🔦 Context
Hit in a production form where a registration-number text input sits next to a date field; Firefox users couldn't type in the text field because focus kept jumping to the year segment.
💻 Code Sample
import {useRef} from 'react';
import {useDateFieldState} from 'react-stately';
import {useDateField, useDateSegment, useLocale} from 'react-aria';
import {createCalendar} from '@internationalized/date';
function Segment({segment, state}) {
let ref = useRef(null);
let {segmentProps} = useDateSegment(segment, state, ref);
return <div {...segmentProps} ref={ref}>{segment.text}</div>;
}
function Field(props) {
let {locale} = useLocale();
let state = useDateFieldState({...props, locale, createCalendar});
let ref = useRef(null);
let {fieldProps} = useDateField(props, state, ref);
return (
<div {...fieldProps} ref={ref} style={{display: 'flex'}}>
{state.segments.map((s, i) => <Segment key={i} segment={s} state={state} />)}
</div>
);
}
export default function App() {
return (
<div>
<input aria-label="text" placeholder="type digits here" />
<Field aria-label="date" />
</div>
);
}
Steps (Firefox): click into a date segment once (or just let the field render), click into the <input>, then type digits → focus jumps into a segment and the digits are captured there.
🌍 Your Environment
| Software |
Version(s) |
| react-aria / @react-aria/datepicker |
Reproduced on @react-aria/datepicker@3.16.0; the selectionchange/collapse code is unchanged on main. |
| Browser |
Firefox (latest). Not reproducible in Chromium/WebKit. |
| Operating System |
Linux / Windows / macOS |
Provide a general summary of the issue here
In Firefox, typing into a regular
<input>that is a sibling of aDateField/DatePickercan move focus into one of the date segments mid-keystroke. Subsequent characters are then captured by that segment (e.g. typed digits land in the year segment) instead of the input the user was actually typing in.🤔 Expected Behavior?
Typing in an unrelated input never moves focus into a date segment. The date field only reacts to input while one of its own segments is focused.
😯 Current Behavior
Focus jumps from the input into a date segment (commonly the last/year segment), and the keystrokes are consumed there.
Root cause is the document-level
selectionchangelistener inuseDateSegment(packages/react-aria/src/datepicker/useDateSegment.ts):Two Firefox-specific behaviors combine:
<input>/<textarea>is not reflected in the documentwindow.getSelection(); the document selection stays wherever it last was in regular DOM content — which, after the segments render/are interacted with, is inside a date segment. SonodeContains(ref.current, selection.anchorNode)is stilltrueeven though focus is in the input.selectionchange, so the handler runs and callsselection.collapse(ref.current). In Firefox, collapsing the selection onto acontentEditablenode moves focus to it (Chromium does not).Result: focus is pulled into the segment while the user types elsewhere. The handler currently re-collapses the selection regardless of whether the segment is the active element — but its purpose (per the existing comment) is only to keep the selection collapsed within a focused segment during Android-Chrome composition.
Note:
user-select: noneon the segment container (the workaround suggested for the click-focus issues #3163 / #3164) does not help here, because the focus move comes from a programmaticSelection.collapse(), not a user selection.💁 Possible Solution
Only re-collapse when the segment is actually the active element:
This preserves the Android-Chrome composition fix (which only matters while the segment is focused) and eliminates the cross-element focus steal. Happy to open a PR (with a browser regression test) — see below.
🔦 Context
Hit in a production form where a registration-number text input sits next to a date field; Firefox users couldn't type in the text field because focus kept jumping to the year segment.
💻 Code Sample
Steps (Firefox): click into a date segment once (or just let the field render), click into the
<input>, then type digits → focus jumps into a segment and the digits are captured there.🌍 Your Environment
@react-aria/datepicker@3.16.0; theselectionchange/collapsecode is unchanged onmain.