Skip to content

fix(focus): handle single radio button in FocusScope#9587

Open
jtstothard wants to merge 4 commits intoadobe:mainfrom
jtstothard:fix/focus-scope-single-radio-crash
Open

fix(focus): handle single radio button in FocusScope#9587
jtstothard wants to merge 4 commits intoadobe:mainfrom
jtstothard:fix/focus-scope-single-radio-crash

Conversation

@jtstothard
Copy link

@jtstothard jtstothard commented Feb 3, 2026

Summary

Fixes #9569

FocusScope crashes with TypeError: (intermediate value) is not iterable when containing a form with exactly one unchecked radio button.

Root Cause

Per the DOM spec, form.elements.namedItem() returns:

  • RadioNodeList (iterable) for 2+ elements with the same name
  • Element (NOT iterable) for exactly 1 element
  • null for no elements

The isTabbableRadio function assumed it always returns RadioNodeList and tried to spread it, causing the crash.

Solution

Refactored into two pure functions with proper type narrowing:

function getRadiosInGroup(element: HTMLInputElement): HTMLInputElement[] {
  // ...
  const radioList = element.form.elements.namedItem(element.name);
  if (radioList instanceof RadioNodeList) {
    return Array.from(radioList).filter(
      (el): el is HTMLInputElement => el instanceof HTMLInputElement
    );
  }
  if (radioList instanceof HTMLInputElement) {
    return [radioList];
  }
  return [];
}

function isTabbableRadio(element: HTMLInputElement): boolean {
  if (element.checked) {
    return true;
  }
  const radios = getRadiosInGroup(element);
}

Key improvements:

  • Extracted getRadiosInGroup as a pure function with early returns
  • No let declarations - only const
  • No type casting - uses instanceof checks and type predicates
  • Explicit return type annotations
  • isTabbableRadio is now a simple composition

Testing

  • Added regression test for single radio button scenario
  • All 58 FocusScope tests pass

Checklist

@jtstothard jtstothard force-pushed the fix/focus-scope-single-radio-crash branch 2 times, most recently from eea74d2 to 0722c48 Compare February 3, 2026 18:21
Fixes adobe#9569

form.elements.namedItem() returns an Element (not RadioNodeList) when
there is exactly one element with that name. This caused TypeError when
trying to spread a non-iterable Element.

The fix checks if namedItem() returns a single Element before spreading,
handling all three possible return types per the DOM spec:
- RadioNodeList (iterable) for 2+ elements with the same name
- Element (NOT iterable) for exactly 1 element
- null for no elements
Copy link
Member

@snowystinger snowystinger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, one change I'm making so that we don't regress on our IFrame support

Copy link
Member

@snowystinger snowystinger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

apologies, it didn't like that i spanned a non-touched line for some reason

@jtstothard
Copy link
Author

@snowystinger thanks for the review, is there any way to add the ready to review tag so others review too?

@snowystinger
Copy link
Member

thanks for the reminder, it's actually just a tag for myself, has no bearing on when others will review it

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FocusScope crashes with single radio button in form

2 participants