[selectors] adding a :has-focus pseudo class

We discussed this briefly at the Sofia F2F, but did not get to the bottom of the issue, so I’d like to reopen.

=== Intro ===

The :hover and :active pseudo classes match more than the currently hovered or active element: they also match on ancestors of the element, and allow the host language to let more elements match, which html uses on labels and their labeled controls. The :focus pseudo class does not do this, but there are cases when you would want something like that. 

Here are some use cases for a :has-focus pseudo class (previously suggested under names such as :contains-focus or :focusWithin), which would work like :focus, except with rules similar to :hover and :active for which elements it matches. I’m also giving details why alternative solutions that have been proposed don’t quite cut it.

=== Use case 1 ===

You are making a UI widget/component that consists of several pieces that are logically one control and you want to show focus around all of it. For example, if you were building an increment entry that showed you its +/- steps as 2 buttons around the input, you might do something like this:

    <div class="increment-widget">
       <div class="decrement-button">-10</div>
       <input class="value" />
       <div class="increment-button">+10</div>
    </div>

You might want the focus border/outline to be around the "increment-widget" div to encompass the inc/dec buttons too, so it looks like a single widget from the user's perspective. The idea is you'd be able to say:

    .value:focus { outline: none; }
    .increment-widget:has-focus { outline: 1px solid blue }

Without :has-focus you would have to resort to JS, and listen to focus/blur events on the input and attach some kind of "focused" class that you target yourself. It's possible, but there are complications with having to do that manually:

You have to be very careful whether the window currently has focus and only apply that "focused" class manually if it does. For example, if in receiving an AJAX response, or a timer going off you decide to focus that input, you only want to apply the "focused" class on the "increment-entry" if document.hasFocus() is true, else you'll get a blue outline even if the browser doesn't have focus which just looks wrong, but then you'd better remember you wanted it to have that focused class and now must listen to window.onfocus/onblur to toggle that focused class on/off to keep that behavior. Very messy, easy to mess up, and not too efficient with all these event handlers. Especially if you're going to have many of these on screen.

=== Use case 1.5 ===

Same as use case 1, except you are using web components and the shadow dom to make your widget. Same difficulties as above, potentially complicated by the shadowiness of things.

=== Use case 2 ===

For forms or input pages that have sections of data or distinct sections of interaction, :has-focus can be used to visually indicate the active area of the screen.  For example, a customer checkout page that has section 1 "Confirm Purchase", with items to buy, and areas to add/remove items, section 2 "Billing Information" with inputs for credit cards, coupon codes, etc.  And a section 3 "Shipping" with address inputs, phone numbers, etc. As the user is tabbing through the screen the sections could light up , change background, or otherwise indicate which section was being worked on.

Implementing this through JS has the same complications as above.

=== Use case 3 ===

When a control which has a label is focused, you may want to highlight both the control and the label.

Implementing this through JS has the same complications as above.

Note that if you want to combine this with case 1 or 2, it gets marginally more complicated as you now have to deal with several converging/overlapping ancestor chains.

=== Why :has() won’t work ===

It has been suggested that this could be handled by :has(:focus), except that:

1) :has() is not part of the fast profile and must not be implemented in dynamic browser selector matching.

2) When using web components with a shadow dom, the hover state pierces through the shadow root, allowing :hover / :active to match ancestors of the web component when a (shadow) child of the component is hovered / active. :has(:focus) would not do that. A more complex selector can be made to deal with this using ::shadow (or /deep/ actually) but this would still depend on :has.

3) like :hover and :active, :has-focus would allow the host language to define additional ways in which an element can match, as html does for labels and labeled controls. :has(:focus) would not mirror that behaviour. A more complex selector could be crafted to handle html labels specifically, but that would not allow the flexibility of allowing the host language to manage the mapping, and would still depend on :has().

=== Conclusion ===

We should add a :has-focus selector, included in the fast profile, which:
a - matches on elements that match :focus
b - matches on parents of elements that match :has-focus
c - allows the host language to define additional ways in which an element can match :has-focus (and then ping the whatwg to do their part)

Alternatively (and I would actually prefer that), we could add these b and c rules to the existing :focus pseudo class. However, given that :focus is already interoperable in not doing that, I am afraid we’d run into web compat problems. Anyone has (or can gather) data to check if we can do that?

=== References ===

This is not the first time that this is being raised. See
http://lists.w3.org/Archives/Public/www-style/2013Apr/0713.html
or Sylvain here:
http://lists.w3.org/Archives/Public/www-style/2013Jul/0294.html
or StackOverflow:
 * http://stackoverflow.com/questions/17957299/css3-style-on-focused-parent-element
 * http://stackoverflow.com/questions/2212583/affecting-parent-element-of-focusd-element-pure-csshtml-preferred
 * http://stackoverflow.com/questions/4388102/can-you-style-an-active-form-inputs-label-with-just-css
 * http://stackoverflow.com/questions/5978239/anyway-to-have-a-label-respond-to-focus-css

 - Florian

Received on Friday, 14 November 2014 17:29:20 UTC