Skip to content

Anchor the :required and :optional tests, and let :optional take a button - #191

Open
jdalton wants to merge 1 commit into
dperini:masterfrom
jdalton:fix/optional-anchors
Open

Anchor the :required and :optional tests, and let :optional take a button#191
jdalton wants to merge 1 commit into
dperini:masterfrom
jdalton:fix/optional-anchors

Conversation

@jdalton

@jdalton jdalton commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

The patterns behind :required and :optional are written /^input|select|textarea$/i, which does not anchor an alternation. A regular expression reads that as ^input or select or textarea$, so it accepts any element whose name starts with "input", any name containing "select", and any name ending in "textarea".

:optional also has to match <button> elements, which it currently skips.

What the loose pattern accepts, and the precedent for the fix

The alternation without a group means an element named inputmode matches ^input, and any custom element whose name contains select matches too. This file already writes the same kind of test correctly elsewhere — :empty and :placeholder-shown use /^(?:input|textarea)$/i — so this change makes the two form patterns match that existing style.

The same shape of bug was fixed in :link and :any-link, where /^a|area$/i accepted <abbr href="…">.

Why a button is optional, and where browsers say so

The HTML spec lists button elements first among the ones :optional matches, before inputs, selects and textareas. Blink answers true for a button without asking anything else, in html_button_element.h#L113, reached from selector_checker.cc#L2751.

A button has no required property, so !e.required is already true for one and adding it to the list is the whole change. button:optional returns the button afterwards, where it returned nothing before.

References: the spec, the browser source, and what each part was reasoned from

This patch applies to master on its own. The sixteen in this series were checked by cherry-picking them onto master one after another, in this order and in reverse, and all sixteen land without a conflict.

@jdalton
jdalton force-pushed the fix/optional-anchors branch from ba25907 to da1d55f Compare September 4, 2026 18:00
…tton

/^input|select|textarea$/ alternates '^input' with 'select' and with 'textarea$' rather than anchoring an alternation, so it accepts any element whose name begins with 'input', any name containing 'select', and any ending in 'textarea'. Both pseudo-classes use it.
 ':optional' also has to match button elements, which the HTML spec lists first among the ones it matches and which Blink answers true for outright. https://github.com/chromium/chromium/blob/155.0.8041.1/third_party/blink/renderer/core/html/forms/html_button_element.h#L113
 A button has no required property, so '!e.required' is true for one and the list is the only change needed.

References:

- Spec: https://html.spec.whatwg.org/#selector-optional — the list of elements that match, which opens with button
- Spec: https://html.spec.whatwg.org/#selector-required — and the list for the other half
- Chromium: https://github.com/chromium/chromium/blob/155.0.8041.1/third_party/blink/renderer/core/html/forms/html_button_element.h#L113 — a button is optional outright
- Chromium: https://github.com/chromium/chromium/blob/155.0.8041.1/third_party/blink/renderer/core/css/selector_checker.cc#L2751 — how ':optional' is dispatched
- MDN: https://developer.mozilla.org/en-US/docs/Web/CSS/:optional
@jdalton
jdalton force-pushed the fix/optional-anchors branch from da1d55f to ba18c39 Compare September 5, 2026 02:48
@jdalton

jdalton commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator Author

This one sits with #190 and #192 and #193, which touch the conformance fixes found by comparing against Chromium.

They do not depend on each other. All seventeen in the series cherry-pick onto master in any order, and I checked that in both directions, so any one of these can land alone. The order below is the one they read best in:

  • #190 makes :enabled the complement of :disabled, fieldsets included.
  • #191 anchors the :required and :optional tests, and lets :optional take a button.
  • #192 makes a fieldset :valid when none of its controls is invalid.
  • #193 makes every built-in element :defined.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant