Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

:active pseudo-class specification doesn't account for children/labels of disabled form elements #6635

Closed
alystair opened this issue Apr 30, 2021 · 22 comments · Fixed by #7465

Comments

@alystair
Copy link

tl;dr :active is being triggered by/within disabled form elements inconsistently (browser dependent). Specification doesn't account for children/labels of disabled form elements edge case.

There are many situations where child elements are added to buttons, icons as a simple example. When the input, button or textarea are given the disabled attribute things become weird.

Demonstration of behaviour: https://codepen.io/alystair/pen/gOgJZaq?editors=1101

  • Chrome:
    • Disabled button only activates :active when clicking children (follows current spec).
    • Pressing disabled input and textarea activates :active (not following spec)
    • Pressing label of disabled element activates :active (?)
  • Firefox: :active triggers in all instances (not following spec)
  • Safari: ?
  • Safari iOS: ?

Current workaround: add pointer-events:none to element (and it's children) with disabled attribute. Less trivial for non-wrapping label elements.

Relevant spec: https://html.spec.whatwg.org/multipage/semantics-other.html#selector-active

:​active✔MDN
The :​active pseudo-class is defined to match an element while an element is being activated by the user.

To determine whether a particular element is being activated for the purposes of defining the :​active pseudo-class only, an HTML user agent must use the first relevant entry in the following list.

  • If the element has a descendant that is currently matching the :​active pseudo-class
    The element is being activated.

...

  • If the element is a button element
  • If the element is an input element whose type attribute is in the Submit Button, Image Button, Reset Button, or Button state
    The element is being activated if it is in a formal activation state and it is not disabled.

Specification should be revised to ensure:

  • Recursion of parent tree to ensure no disabled form element (perhaps any element?) is located.
  • Form element associated with targeted label does not have disabled state.

Somewhat related (and I don't mean to sidetrack):

"A form control that is disabled must prevent any click events that are queued on the user interaction task source from being dispatched on the element."

This works as intended when the event listener is attached to the element itself. However when the event handler is attached to a parent, children of disabled elements 'leak' through - is this intentional?

@annevk annevk transferred this issue from whatwg/dom Apr 30, 2021
@annevk
Copy link
Member

annevk commented Apr 30, 2021

cc @whatwg/css @whatwg/forms

@emilio
Copy link
Contributor

emilio commented Apr 30, 2021

Hmm, but you can pretty much out an "activatable" element inside a disabled button, think of a help link or something. So going through the parent chain doesn't work unless I'm missing something.

Special-casing disabled + :active here is not great, IMO. Disabled elements do match :hover for example, why would they not match :active?

@alystair
Copy link
Author

alystair commented May 1, 2021

Because the spec clearly states:

If the element is an input element whose type attribute is in the Submit Button, Image Button, Reset Button, or Button state
The element is being activated if it is in a formal activation state and it is not disabled.

Logically it makes no sense that a disabled element can be activated, it's disabled. If you want to explain why something is disabled shouldn't it be external to the disabled element, especially if it requires interactivity?

@annevk
Copy link
Member

annevk commented May 3, 2021

"Activation behavior" (which is basically the click event) does have some special cases around disabled elements, see also #1576.

Now whether that should be tied to :active as well is another matter.

cc @saschanaz

@saschanaz
Copy link
Member

saschanaz commented May 3, 2021

The disabled condition is added by 9985973 back in 2015 but it's only implemented by Blink. WebKit behaves as Gecko does (at least on Windows); it always marks the input elements as active regardless of the disabled attribute.

Since it's already not very consistent with other <input type> maybe we should revert it, unless it's somehow important for accessibility?

@annevk
Copy link
Member

annevk commented May 4, 2021

@whatwg/a11y thoughts?

@patrickhlauke
Copy link
Member

naively, as an author i'd expect that something marked as disabled - regardless of whether you're trying to activate it directly, its child elements, its label - would not get :active applied ever, if thinking of :active as only referring to "this is about to execute"

if looking at :active as less of a visual "this is about to execute" and more as a "this is about to execute unless it's disabled or otherwise prevented", then i would expect :active to then consistently be applied regardless of whether i'm trying to activate the disabled thing itself, a child element, or clicking/activating its label somehow.

to aid authors in building their conceptual model of what :active represents (from the two choices above), it would be good to be consistent either way. currently, just going by what chrome does in https://codepen.io/alystair/pen/gOgJZaq?editors=0011, it seems rather more arbitrary that a disabled <button> when clicked doesn't get :active, but clicking a label pointing to it or a child element of the button does.

as an aside, from the JS side, the weird "clicking the disabled button itself doesn't fire click, but clicking a child element of it, or a label to it, does" current behaviour is also quite confounding.

Hmm, but you can pretty much out an "activatable" element inside a disabled button, think of a help link or something. So going through the parent chain doesn't work unless I'm missing something.

you shouldn't really have an actionable element wrapped inside a disabled element - like a proper <a href="..."> link, or a nested <button>, or similar - as that's invalid markup (and no, triggering something that's normally not interactive, like an <i> element - even if you also added tabindex="0" to make it work for keyboard users - might be initially valid, but you'd also need to give the interactive control an explict role="..." like role="button" at which point validators will also rightly complain that you shouldn't have nested controls like that ... so long story short this should never actually be done full stop).

from an accessibility point of view, there's no SC directly in WCAG that speaks to the need/behaviour of :active or similar. but going beyond pure WCAG and into usability/UX users are likely to be confused by inconsistent, seemingly arbitrary, behaviour.

@alystair alystair changed the title :active pseudo-class specification doesn't account for children of disabled form elements :active pseudo-class specification doesn't account for children/labels of disabled form elements May 4, 2021
@alystair
Copy link
Author

alystair commented May 4, 2021

Although what @saschanaz said seemed counter-intuitive to me initially, the longer I think about it - the more it made sense from an implementation perspective. It allows easier visual consistency across browsers (less specialized rule logic) compared to adding additional corrective spec for conditionally triggering :active 'correctly'.

Chromium user agent stylesheet would need revision to ensure no visual change happens for activated disabled form elements once this is implemented.

Seems to be the path of least resistance (the JS aside on the other hand...).

@alystair
Copy link
Author

What would be the theoretical next step? Does it become a topic of discussion at an upcoming meeting of sorts? Would like to learn more about the process here :)

@zcorpan zcorpan added the agenda+ To be discussed at a triage meeting label Sep 30, 2021
@zcorpan
Copy link
Member

zcorpan commented Sep 30, 2021

I added agenda+ to add it to the next triage meeting.

The UA stylesheet is easy to update. The web legacy using button:active (etc) styling and expecting it to not apply on disabled buttons is harder. However, since Firefox has this behavior already, maybe it's at least web compatible enough or doesn't confuse users enough that they've had to change.

Are there known bugs or complaints about Firefox's behavior?

@patrickhlauke
Copy link
Member

maybe it's at least web compatible enough or doesn't confuse users enough that they've had to change

anecdotally, on Bootstrap we've had to add pointer-events:none some time ago to work around this weirdly illogical behavior (in addition to doing it for when authors for whatever reason decide to create faked disabled buttons using <a> elements to suppress mouse behavior) https://getbootstrap.com/docs/5.1/components/buttons/#disabled-state

I have no doubt that authors hitting the weird behavior in their own projects have hacked around enough to suppress this in similar ways. despite this, it'd be more sustainable to fix this at the root ...

@zcorpan
Copy link
Member

zcorpan commented Oct 7, 2021

Safari seems to behave like Firefox: clicking the disabled button matches :active

@domenic
Copy link
Member

domenic commented Nov 11, 2021

I think the root of the issue here is that you could imagine two meanings of "active":

  1. "Is being activated", which is roughly the current spec's "in a formal activation state" + "being actively pointed at".
  2. "Is being successfully activated", i.e. had its activation behavior triggered and done something useful (not short-circuited due to disabledness or similar)

My reading is that the spec is trying to go for (2), but doing it poorly, and that hasn't been implemented very well. Thus all the text about disabled, and talking about having activation behavior instead of just keying off of user intent to activate.

@emilio points out that (1) is much more consistent with other similar pseudo-classes, such as :hover. Those don't try to add a bunch of heuristics to determine whether something is "successfully" hovered; if the user hovers, then :hover triggers. Similarly, he argues, if the user clicks/touches/tries to activate with the keyboard/etc., :active should trigger.

I agree with @patrickhlauke that getting something consistent, which developers can build a good mental model around, is the highest priority here. So I support aligning the spec with 2/3 browsers (Safari and Firefox), around definition (1). I hope we can update Chrome to match.

Separately, it sounds like there might be interest in authors in telling whether something can be successfully activated, in the sense of (2). For this I'd suggest a separate proposal, e.g. a pseudo-class :has-activation-behavior. (Or perhaps even :clickable, playing off the fact that on the web platform even keyboard "activation" fires a click event.) Designing that would require a good deal of work, IMO, as we think through all the tricky cases around not only disabled and descendants, but also other interesting ways something can be made non-activatable. E.g., does pointer-events: none impact things? How about inertness? Does the distinction between disabled and actually disabled matter here? Etc.

@ljharb
Copy link
Contributor

ljharb commented Nov 11, 2021

What’s the use case for 1? I can’t think of any use case for active that isn’t exclusively 2.

@domenic
Copy link
Member

domenic commented Nov 11, 2021

It's pretty common to want to style things that are clicked, even if clicking them does nothing (besides change the style).

@ljharb
Copy link
Contributor

ljharb commented Nov 11, 2021

“that are clicked” can refer to the click event, sure, but otherwise it would mean “things that are successfully clicked”, which a disabled button can never be. (and I’m skeptical anyone wants to use the active pseudo class to target unclickable things)

@emilio
Copy link
Contributor

emilio commented Nov 14, 2021

It's pretty common to have active state on list items, spans, divs, or other things that can potentially receive mousedown events that get handled via JS. E.g., https://material.io/components/cards has an example where the "Elevated" and "Outlined" tabs are just <span>s but have active-like styling.

Whether those should conceptually be <button>s or what not is probably a good question, but I believe there's a usecase for using :active on non-otherwise-activatable things (and all engines do it).

Plus, :active, much like :hover applies to the whole chain of elements from the thing being activated, which of course includes otherwise non-activatable things (e.g., body:active {background: red} works in all browsers no matter whether you click on a button or not).

So unless we're discussing to more fundamentally change how :active works, I think the consistent thing is to make that apply to disabled form elements.

@domenic
Copy link
Member

domenic commented Jan 5, 2022

I found an interesting other non-interop: https://jsbin.com/luwicesuwu/1/edit?html,output

My question was: the spec currently has a hard-coded list of elements which could match :active through "in a formal activation state". Could we just remove that and use "any element which has activation behavior"?

My test case was an <input type="radio">. Per the current spec, this should not match active while you are using the spacebar (for example) to trigger activation behavior. In Firefox this is true; only clicking (i.e. "being actively pointed at") makes :active match. In Chrome this is false, and :active matches while the spacebar is being held down.

(Unfortunately I went into another lockdown here in NYC and left my testing Mac at the office, sigh...)

I like Chrome's behavior more, both in how it makes the spec simpler and in how it seems good to give keyboard users the same experience as mouse users for all activatable controls, not just a hard-coded list. What do others think?

domenic added a commit that referenced this issue Jan 5, 2022
This matches Gecko and WebKit. The disabled condition was added to the spec in 9985973 but not implemented uniformly. As discussed in #6635, it doesn't work very well and is inconsistent with other similar pseudo-classes, like :hover.

This also removes the consideration of <link> elements with href="" attributes, since they no longer have activation behavior as of 1e0ee7f. And it updates the definitions of both :active and :hover to be based on the latest Selectors spec, which notably includes the ancestor condition over the flat tree at the CSS layer, instead of having HTML handle that.

There remains some lack of interop around elements such as <input type=radio> which have activation behavior but are not on the list of elements for which "in a formal activation state" is considered. #6635 stays open to track that case.
domenic added a commit that referenced this issue Jan 5, 2022
This matches Gecko and WebKit. The disabled condition was added to the spec in 9985973 but not implemented uniformly. As discussed in #6635, it doesn't work very well and is inconsistent with other similar pseudo-classes, like :hover.

This also removes the consideration of <link> elements with href="" attributes, since they no longer have activation behavior as of 1e0ee7f. And it updates the definitions of both :active and :hover to be based on the latest Selectors spec, which notably includes the ancestor condition over the flat tree at the CSS layer, instead of having HTML handle that.

There remains some lack of interop around elements such as <input type=radio> which have activation behavior but are not on the list of elements for which "in a formal activation state" is considered. #6635 stays open to track that case.
domenic added a commit that referenced this issue Jan 5, 2022
This matches Gecko and WebKit. The disabled condition was added to the spec in 9985973 but not implemented uniformly. As discussed in #6635, it doesn't work very well and is inconsistent with other similar pseudo-classes, like :hover.

This also removes the consideration of <link> elements with href="" attributes, since they no longer have activation behavior as of 1e0ee7f. And it updates the definitions of both :active and :hover to be based on the latest Selectors spec, which notably includes the ancestor condition over the flat tree at the CSS layer, instead of having HTML handle that.

There remains some lack of interop around elements such as <input type=radio> which have activation behavior but are not on the list of elements for which "in a formal activation state" is considered. #6635 stays open to track that case.
@domenic domenic added the agenda+ To be discussed at a triage meeting label Jan 5, 2022
@tabatkins
Copy link
Collaborator

Without knowing if there's any particular history driving the current behavior, yeah, that seems to make sense to me.

@domenic domenic added agenda+ To be discussed at a triage meeting and removed agenda+ To be discussed at a triage meeting labels Jan 11, 2022
@alystair
Copy link
Author

alystair commented Jan 24, 2022

@domenic just saw the triage meeting minutes, I haven't been available to revise the behaviour demonstration that was discussed at the previous triage meeting - life has been getting in the way. Thanks for keeping it alive.

Linking to PR for reference: #7465 - will review and join discussion if I find the time ⌚

@domenic
Copy link
Member

domenic commented Feb 3, 2022

I've decided to split the issue discussed in #6635 (comment) into a separate issue, #7578.

For this issue, #7465 remains the relevant PR. That is ready to merge but is missing web platform tests before we can do so. When we merge it it will close this particular issue.

@past past removed the agenda+ To be discussed at a triage meeting label Feb 3, 2022
domenic added a commit that referenced this issue Feb 10, 2022
This removes the disabled condition for :active, matching Gecko and WebKit. The condition was added to the spec in 9985973 but not implemented uniformly. As discussed in #6635, it doesn't work very well and is inconsistent with other similar pseudo-classes, like :hover.

This also removes the consideration of <link> elements with href="" attributes for :active, since they no longer have activation behavior as of 1e0ee7f.

Finally, this updates the definitions of both :active and :hover to be based on the latest Selectors spec, which notably includes the ancestor condition over the flat tree at the CSS layer, instead of having HTML handle that. This required splitting up "being activated" and "matches :active", as well as "designated" and "matches :hover".

Closes #6635, although note that there remain other interop issues with :active, tracked in #7578.
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Feb 11, 2022
We are changing this behavior because other browsers don't have disabled
conditioning for :active and because it will become more consistent with
other pseudo classes:
whatwg/html#6635 (comment)

Fixed: 1287171
Change-Id: Idab2abbbc94cc73fac70e34ef391c5d63518d569
aarongable pushed a commit to chromium/chromium that referenced this issue Feb 11, 2022
We are changing this behavior because other browsers don't have disabled
conditioning for :active and because it will become more consistent with
other pseudo classes:
whatwg/html#6635 (comment)

Fixed: 1287171
Change-Id: Idab2abbbc94cc73fac70e34ef391c5d63518d569
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3453424
Commit-Queue: Joey Arhar <jarhar@chromium.org>
Reviewed-by: Mason Freed <masonf@chromium.org>
Cr-Commit-Position: refs/heads/main@{#970008}
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Feb 11, 2022
We are changing this behavior because other browsers don't have disabled
conditioning for :active and because it will become more consistent with
other pseudo classes:
whatwg/html#6635 (comment)

Fixed: 1287171
Change-Id: Idab2abbbc94cc73fac70e34ef391c5d63518d569
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3453424
Commit-Queue: Joey Arhar <jarhar@chromium.org>
Reviewed-by: Mason Freed <masonf@chromium.org>
Cr-Commit-Position: refs/heads/main@{#970008}
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Feb 11, 2022
We are changing this behavior because other browsers don't have disabled
conditioning for :active and because it will become more consistent with
other pseudo classes:
whatwg/html#6635 (comment)

Fixed: 1287171
Change-Id: Idab2abbbc94cc73fac70e34ef391c5d63518d569
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3453424
Commit-Queue: Joey Arhar <jarhar@chromium.org>
Reviewed-by: Mason Freed <masonf@chromium.org>
Cr-Commit-Position: refs/heads/main@{#970008}
mattwoodrow pushed a commit to mattwoodrow/wpt that referenced this issue Feb 15, 2022
We are changing this behavior because other browsers don't have disabled
conditioning for :active and because it will become more consistent with
other pseudo classes:
whatwg/html#6635 (comment)

Fixed: 1287171
Change-Id: Idab2abbbc94cc73fac70e34ef391c5d63518d569
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3453424
Commit-Queue: Joey Arhar <jarhar@chromium.org>
Reviewed-by: Mason Freed <masonf@chromium.org>
Cr-Commit-Position: refs/heads/main@{#970008}
moz-v2v-gh pushed a commit to mozilla/gecko-dev that referenced this issue Feb 26, 2022
…atching, a=testonly

Automatic update from web-platform-tests
Remove disabled condition from :active matching

We are changing this behavior because other browsers don't have disabled
conditioning for :active and because it will become more consistent with
other pseudo classes:
whatwg/html#6635 (comment)

Fixed: 1287171
Change-Id: Idab2abbbc94cc73fac70e34ef391c5d63518d569
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3453424
Commit-Queue: Joey Arhar <jarhar@chromium.org>
Reviewed-by: Mason Freed <masonf@chromium.org>
Cr-Commit-Position: refs/heads/main@{#970008}

--

wpt-commits: e3cb675e1b16603aa51051de59cfd4056d89a04d
wpt-pr: 32804
DanielRyanSmith pushed a commit to DanielRyanSmith/wpt that referenced this issue Feb 28, 2022
We are changing this behavior because other browsers don't have disabled
conditioning for :active and because it will become more consistent with
other pseudo classes:
whatwg/html#6635 (comment)

Fixed: 1287171
Change-Id: Idab2abbbc94cc73fac70e34ef391c5d63518d569
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3453424
Commit-Queue: Joey Arhar <jarhar@chromium.org>
Reviewed-by: Mason Freed <masonf@chromium.org>
Cr-Commit-Position: refs/heads/main@{#970008}
jamienicol pushed a commit to jamienicol/gecko that referenced this issue Mar 1, 2022
…atching, a=testonly

Automatic update from web-platform-tests
Remove disabled condition from :active matching

We are changing this behavior because other browsers don't have disabled
conditioning for :active and because it will become more consistent with
other pseudo classes:
whatwg/html#6635 (comment)

Fixed: 1287171
Change-Id: Idab2abbbc94cc73fac70e34ef391c5d63518d569
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3453424
Commit-Queue: Joey Arhar <jarhar@chromium.org>
Reviewed-by: Mason Freed <masonf@chromium.org>
Cr-Commit-Position: refs/heads/main@{#970008}

--

wpt-commits: e3cb675e1b16603aa51051de59cfd4056d89a04d
wpt-pr: 32804
moz-v2v-gh pushed a commit to mozilla/gecko-dev that referenced this issue Mar 8, 2022
…atching, a=testonly

Automatic update from web-platform-tests
Remove disabled condition from :active matching

We are changing this behavior because other browsers don't have disabled
conditioning for :active and because it will become more consistent with
other pseudo classes:
whatwg/html#6635 (comment)

Fixed: 1287171
Change-Id: Idab2abbbc94cc73fac70e34ef391c5d63518d569
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3453424
Commit-Queue: Joey Arhar <jarhar@chromium.org>
Reviewed-by: Mason Freed <masonf@chromium.org>
Cr-Commit-Position: refs/heads/main@{#970008}

--

wpt-commits: e3cb675e1b16603aa51051de59cfd4056d89a04d
wpt-pr: 32804
jamienicol pushed a commit to jamienicol/gecko that referenced this issue Mar 8, 2022
…atching, a=testonly

Automatic update from web-platform-tests
Remove disabled condition from :active matching

We are changing this behavior because other browsers don't have disabled
conditioning for :active and because it will become more consistent with
other pseudo classes:
whatwg/html#6635 (comment)

Fixed: 1287171
Change-Id: Idab2abbbc94cc73fac70e34ef391c5d63518d569
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3453424
Commit-Queue: Joey Arhar <jarhar@chromium.org>
Reviewed-by: Mason Freed <masonf@chromium.org>
Cr-Commit-Position: refs/heads/main@{#970008}

--

wpt-commits: e3cb675e1b16603aa51051de59cfd4056d89a04d
wpt-pr: 32804
mfreed7 pushed a commit to mfreed7/html that referenced this issue Jun 3, 2022
This removes the disabled condition for :active, matching Gecko and WebKit. The condition was added to the spec in 9985973 but not implemented uniformly. As discussed in whatwg#6635, it doesn't work very well and is inconsistent with other similar pseudo-classes, like :hover.

This also removes the consideration of <link> elements with href="" attributes for :active, since they no longer have activation behavior as of 1e0ee7f.

Finally, this updates the definitions of both :active and :hover to be based on the latest Selectors spec, which notably includes the ancestor condition over the flat tree at the CSS layer, instead of having HTML handle that. This required splitting up "being activated" and "matches :active", as well as "designated" and "matches :hover".

Closes whatwg#6635, although note that there remain other interop issues with :active, tracked in whatwg#7578.
mjfroman pushed a commit to mjfroman/moz-libwebrtc-third-party that referenced this issue Oct 14, 2022
We are changing this behavior because other browsers don't have disabled
conditioning for :active and because it will become more consistent with
other pseudo classes:
whatwg/html#6635 (comment)

Fixed: 1287171
Change-Id: Idab2abbbc94cc73fac70e34ef391c5d63518d569
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3453424
Commit-Queue: Joey Arhar <jarhar@chromium.org>
Reviewed-by: Mason Freed <masonf@chromium.org>
Cr-Commit-Position: refs/heads/main@{#970008}
NOKEYCHECK=True
GitOrigin-RevId: ed8595a55c008ac96404f2d95408f59c9a3833b6
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging a pull request may close this issue.