fix: [#2397] <input type=radio checked> added via innerHTML does not uncheck its group - #2398
Open
marciomazza wants to merge 5 commits into
Open
fix: [#2397] <input type=radio checked> added via innerHTML does not uncheck its group#2398marciomazza wants to merge 5 commits into
<input type=radio checked> added via innerHTML does not uncheck its group#2398marciomazza wants to merge 5 commits into
Conversation
…tribute changes
A checked `<input type=radio>` added via innerHTML, insertAdjacentHTML, DOMParser,
templates or setAttribute('checked') did not uncheck the other radio buttons in its
group; only assigning the IDL `checked` property did.
Extract the sibling-unchecking loop from #setChecked into
#uncheckOtherRadioButtonsInGroup() and run it from the attribute pipeline
(onSetAttribute, for `checked` added or `type`/`name` parsed after it) and from
connectedToNode (radio parsed into a detached fragment first). The `checked` attribute
drives checkedness only on the absent->present transition and only while not overridden
via the IDL property, so re-setting an already-present attribute is a no-op and a radio
unchecked by mutual exclusion is not re-checked by its attribute until a form reset,
matching Chrome and Firefox.
Rename #uncheckOtherRadioButtonsInGroup and clear this element's own query-selector cache inside it, so every entry point (attribute parse, connect, IDL setter) gets it without repeating the call.
When a subtree with several checked members of a radio group is connected, connectedToNode walked the group per child and the first checked one won. Browsers keep the last in tree order. Each connecting radio now steps aside for a later checked sibling that is still mid-insertion (isConnected false), and a checked radio inserted before an already-connected one still wins. Extract the group lookup into #getRadioButtonGroup().
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Resolves #2397
A checked
<input type="radio">added to a group viainnerHTML,insertAdjacentHTML,DOMParser,<template>, a shadow root, orsetAttribute('checked', '')does notuncheck the other radios in the group.
Root cause: the radio button group mutual-exclusion algorithm only ran from the
checkedIDL setter (
#setChecked). Parsing, tree insertion, andsetAttributenever triggeredit.
Fix: extract the sibling-unchecking loop into
#reconcileRadioButtonGroup()and also runit from
[onSetAttribute](whencheckedis added, ortype/nameis parsed aftercheckedin the start tag) and[connectedToNode](a radio parsed into a detachedfragment reconciles on connect; when several checked members of a group connect together,
the last in tree order wins, as in browsers). The
checkedcontent attribute drivescheckedness only on the absent→present transition and only while
checkedhas not beenset via the IDL property, so re-setting an already-present attribute is a no-op and a
radio unchecked by the group is not re-checked by its attribute until a form reset.
Deliberate deviation: exclusion is eager, so a detached
<template>/DocumentFragmentbriefly shows only the last checked radio (the state after connecting matches browsers).
Drive-by: the old loop unchecked every same-name radio, not just the checked ones, and
never cleared their
:checkedquery cache.Tests
Fails before / passes after for the repro. Added 12 tests: exclusion after HTML parsing
(last-checked wins, per-group, per-form and per-shadow-root scoping, reconciling against a
radio already checked in a live container), after
insertAdjacentHTML, aftersetAttribute('checked'), on connecting a subtree with several checked members (last intree order wins; a radio inserted before an already-connected one still wins), a
re-set/removed-and-re-added attribute not overriding a group-unchecked radio, a
form.reset()restoring one from its attribute, and a cached:checkedquery stayingfresh. Full
npm testpasses (302 files / 7731 inpackages/happy-dom).Details
Verified in Chrome 152 (headless, DevTools console) and Firefox (
about:blankconsole):after
innerHTMLwith two checked radios only the last stays checked; afterb.checked = truethena.setAttribute('checked', ''),astays unchecked andbchecked; after
form.reset(),ais restored from itscheckedattribute. Safari notchecked (no macOS available); Blink + Gecko agree.
AI
I used Claude Code to write this, over multiple iterations. I guided and reviewed
it myself at every step.
Before submitting the PR, please make sure you do the following:
Tests
innerHTMLwith two checked radios only the last stays checked; afterb.checked = truethena.setAttribute('checked', ''),astays unchecked; afterform.reset(),ais restored from its attribute. Matches the fixed behavior.npm testlocally to make sure that all tests pass before submitting the PR.Title
feat,fix,choreorBREAKING CHANGE. The issue is optional and can be omitted if the pull request does not relate to an issue.AI tools