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

How to implement and shape API for <selectedoption> element for <select> #10242

Open
josepharhar opened this issue Apr 23, 2024 · 14 comments
Open

Comments

@josepharhar
Copy link
Contributor

josepharhar commented Apr 23, 2024

In the proposed appearance:base improvements for <select>, we want to support the use case of rendering the full DOM contents of the currently selected <option> into the select's button and let the author style it differently from the way it is styled in the <option> without the use of script.

The currently proposed way to support this, as designed in OpenUI, is to have a <selectedoption> element which copies contents from the selected <option> and replaces its children with those contents every time that the selected <option> changes.

There have been two ways of doing this which have been discussed:

  1. Call cloneNode on the selected <option>, and replace the light DOM children of <selectedoption> with the result of cloneNode.
    • Authors can style the <option> and <selectedoption> separately by selecting for those elements in their selectors. Since the cloned contents are in the light DOM, there is no new syntax needed.
  2. <selectedoption> has a UA shadowroot. Call cloneNode on the selected <option>, and replace the contents of the <selecedoption>'s shadowroot with the cloneNode result. Since this could leak the shadowroot, we will use the proposed sanitizer API on the cloneNode result before appending it to the shadowroot. In order to let authors style all the content in the shadowroot, we will create a pseudo element which goes to the content of the UA shadowroot, and add new syntax to CSS to allow for these descendants of the pseudo element to be selected.

Here's a quick example which shows the usecase and what the API would be like in solution 1 vs solution 2. It is a country picker where we only want to render the picture of the country flag in the button without the text next to it in the dropdown.

<select>
  <button type=popover>
    <selectedoption></selectedoption>
  </button>
  <datalist>
    <option>
      <img src="country1.jpg">
      <span>Country 1</span>
    </option>
    <option>
      <img src="country2.jpg">
      <span>Country 2</span>
    </option>
  </datalist>
</select>

Solution 1 CSS:

selectedoption > span {
  display: none;
}

Solution 2 CSS:

selectedoption::selectedoption-content > span {
  display:none;
}

This topic was previously discussed here: #9284

@josepharhar josepharhar changed the title How to implement shape API for <selectedoption> element for <select> How to implement and shape API for <selectedoption> element for <select> Apr 23, 2024
@tabatkins
Copy link
Member

Just curious - is there a reason this was split from #9284? If it was just to reset the conversation given a more stable design from HTML/OpenUI, should we close #9284?


I don't have a strong opinion between these two. I'm happy that we seem to have settled on "copy the DOM over" in both solutions, and are just debating whether it should be visible in the light DOM or hidden by a UA shadow; that makes many potential questions much easier to answer.

The Selectors spec is already equipped to handle Option 2, if we want: see Pseudo-elements/internal structure and the <complex-selector> grammar. This would be the first pseudo-element to actually use that syntax.

I suspect we might want Option 2, not for CSS reasons, but just for DOM reasons - it avoids the need to answer questions about mutation events and the like (I think those are censored by shadows? especially UA shadows?)

@josepharhar
Copy link
Contributor Author

Just curious - is there a reason this was split from #9284? If it was just to reset the conversation given a more stable design from HTML/OpenUI, should we close #9284?

Yeah I think we can close #9284 in favor of this.

it avoids the need to answer questions about mutation events and the like (I think those are censored by shadows? especially UA shadows?)

We could just disable mutation events during the scope of this DOM copying. We are trying to disable mutation events entirely anyway, so disabling them in this case sounds very reasonable to me.

Another question I have if we go with option 2: The pseudo-element would have to target the ShadowRoot node in order to access all of its children independently. Is it possible to have a pseudo-element map to a ShadowRoot node which is technically not an element?

@tabatkins
Copy link
Member

We could just disable mutation events during the scope of this DOM copying. We are trying to disable mutation events entirely anyway, so disabling them in this case sounds very reasonable to me.

Ah, true, that makes sense. Then that's not a factor either way, I'd think.

Another question I have if we go with option 2: The pseudo-element would have to target the ShadowRoot node in order to access all of its children independently. Is it possible to have a pseudo-element map to a ShadowRoot node which is technically not an element?

Yes, that's conceptually fine. It's just an "element" that is prevented from ever generating a box.

But the inheritance story is a little funky in that case. Would the elements inherit from the pseudo-element? That hasn't happened before (only pseudo-to-pseudo), and I'm not sure if it's problematic in implementations or not. Just using light-DOM children instead avoids us having to answer any of these questions.

@annevk
Copy link
Member

annevk commented Jun 14, 2024

How often is cloning done when an option (that's also the selected option) is being mutated? I.e., what is the timing story. And if you mutate the selected option, is that reflected?

I'm also wondering about the timing of the first time an option gets cloned when the end user hasn't made a choice yet.

@josepharhar
Copy link
Contributor Author

How often is cloning done when an option (that's also the selected option) is being mutated? I.e., what is the timing story.

Right now my prototype does it synchronously but I'd like to try building on MutationObserver which from my understanding is like microtask timing right?

And if you mutate the selected option, is that reflected?

If the selected <option> has any descendant mutated, then yeah we want that to trigger a clone into <selectedoption>. If the author mutates the contents of <selectedoption> then I'm not planning on doing anything.

I'm also wondering about the timing of the first time an option gets cloned when the end user hasn't made a choice yet.

We can update the contents of <selectedoption> when it is inserted into the document. Does that answer your question?

@annevk
Copy link
Member

annevk commented Jun 14, 2024

Thanks! Right, microtask timing might be preferable to avoid cloning too often. As for your last point, <selectedoption> is inserted before any of <option> elements, so that does not address my question. But I guess we need to watch for mutations to those <option> elements, so maybe that would fall out automatically.

@josepharhar
Copy link
Contributor Author

Thanks! Right, microtask timing might be preferable to avoid cloning too often. As for your last point, <selectedoption> is inserted before any of <option> elements, so that does not address my question. But I guess we need to watch for mutations to those <option> elements, so maybe that would fall out automatically.

When the <option> element is inserted, the <selectedoption> will be updated to match the new <option> if the new option considered to be the selected option by the <select>. Any time that the <select> thinks that the currently selected option is changed, then it will update the contents of <selectedoption>, and <option> insertion falls into that category.

@annevk
Copy link
Member

annevk commented Jul 1, 2024

One additional thought here: once we do <select multiple> will we regret aspects of this API?

I guess in particular if we do <select multiple> and also add a feature HTML arguably doesn't have (but some implementations do) whereby you don't render the control fully inline but still have a dropdown. (Because in the "normal" <select multiple> case there's no <button> presumably.)

@josepharhar
Copy link
Contributor Author

I don't see how this proposal limits what we can do for select multiple. I think that for now, <selectedoption> shouldn't do anything when the select has the multiple attribute.

@annevk
Copy link
Member

annevk commented Jul 17, 2024

My concern is not that it limits what we can do, my concern is that the end result does not feel like a unified whole.

@josepharhar
Copy link
Contributor Author

Does figuring out how to represent multiple options in <selectedoption> for <select multiple> address your concern?

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed How to implement and shape API for `<selectedoption>` element for `<select>` , and agreed to the following:

  • RESOLVED: move forward with the light dom cloning api shape and discuss timing in the issue
The full IRC log of that discussion <gregwhitworth> jarhar: there was a CSS issue where we briefly discussed this and the usecase is that if you are making a customizable select element you want to have a button that is fully styleable and contains rich content within the button and style it differently than the content that is in the option
<gregwhitworth> jarhar: if you have a country picker and the option only has the flag representation but in the selectedtoption shows the other DOM content within it. When the option is changed the contents of the option itself will be changed with the selected option's DOM content
<gregwhitworth> jarhar: the way I've implemented this in the Chromium POC is to use the cloneNode API and it works pretty well
<gregwhitworth> jarhar: in the past we've discussed using a useragent shadow and the sanitizer API
<gregwhitworth> jarhar: this would require us to complete the sanitizer API and a new CSS syntax
<jarhar> q?
<emilio> q+
<smaug> q+
<dandclark> q+
<gregwhitworth> emilio: what is the behavior if you don't have <selectedoption> element
<astearns> ack emilio
<gregwhitworth> jarhar: if you don't provide a button at all then you'll get the same behavior you get today where the text content is copied into the button. If you provide the button without the selectedoption element then it won't be copied
<masonf> q+
<gregwhitworth> emilio: my only concern was if you were going to change the shadow DOM. It feels kind of clunky to change the Light DOM and all of its benefits but I don't have a better proposal
<astearns> ack smaug
<gregwhitworth> smaug: I think the light DOM option might be fine. This is similar to SVG use
<gregwhitworth> smaug: in Gecko it re-clones when we do style flush so you kind of want something like that here and since Anne was asking about scheduling
<gregwhitworth> emilio: do we want to handle dynamic mutations to the selectedoption
<gregwhitworth> jarhar: we want to keep up with any mutations on the element itself which Chromium uses today
<gregwhitworth> jarhar: the mutation observer will catch the mutation and clone the content into the shadowRoot of the button
<gregwhitworth> emilio: the key tricky bit is when this should run
<gregwhitworth> emilio: it should be similar to mutation and changeEvent case
<gregwhitworth> emilio: if we use mutation observer timing
<masonf> q-
<gregwhitworth> smaug: it would be odd that it wouldn't get the correct layout information
<gregwhitworth> emilio: in Gecko we have internal mutation observers that happens synchronously
<gregwhitworth> smaug: whenever layout is flushed we do the actual clone
<gregwhitworth> emilio: it is odd to do DOM observable changes at style flush time
<gregwhitworth> emilio: I don't think that would be a viable option here
<gregwhitworth> smaug: if you're updating Light DOM all of the other observers will still work so they should work as expected
<gregwhitworth> emilio: if you modify the option and read back the bounding client rect you should have the same issue
<gregwhitworth> q?
<masonf> q+
<gregwhitworth> chrishtr: if someone were to polyfill this with mutation observers they would get the experience?
<gregwhitworth> emilio: in jarhar example you grow the size of the icon or something upon cloning and you get bounding rect on the element you would need to wait for the post microtask
<gregwhitworth> astearns: do we need to have more discussions about timing in the issue or can we resolve it?
<masonf> q-
<gregwhitworth> emilio: I think we should discuss it async as there are tradeoffs
<gregwhitworth> chrishtr: is it ok to conclude that the approach we're taking is ok but we need to resolve on the timing?
<gregwhitworth> emilio: I suppose so, but if the timing is not right then it may make it so then it may require us re-thinking the light DOM cloning
<gregwhitworth> chrishtr: what considerations would make it "not fine"?
<gregwhitworth> smaug: in theory we could use custom element reaction timing which is sooner. We have options but haven't gone over them
<gregwhitworth> astearns: let's set timing aside for a minute
<astearns> ack dandclark
<gregwhitworth> dandclark: I added myself to the queue to support the light DOM solution since it's all author controlled content
<gregwhitworth> dandclark: I would like to be able to explain the timing with author defined APIs and it will be easier to understand
<gregwhitworth> dandclark: we can discuss that more in the issued
<chrishtr> +1 to microtask timing
<gregwhitworth> emilio: custom element timing seems worth exploring and would make things more consistent
<dbaron> s/custom element/custom element reaction/
<gregwhitworth> emilio: it is an issue beyond layout but any type of DOM interrogation
<gregwhitworth> astearns: we can resolve that we want to move forward with this shape but discuss the timing more
<gregwhitworth> astearns: any other concerns?
<emilio> +1 to light dom if there is a reasonable timing for it
<jarhar> proposed resolution: move forward with the light dom cloning api shape and discuss timing in the issue
<masonf> +1
<gregwhitworth> +1
<jarhar> RESOLVED: move forward with the light dom cloning api shape and discuss timing in the issue
<gregwhitworth> smaug: Anne was asking about the solution for multiple
<gregwhitworth> astearns: and maybe a seperate issue

@josepharhar
Copy link
Contributor Author

I filed a separate issue to discuss timing here: whatwg/html#10520

I filed it in whatwg because it doesn't seem to have much to do with CSS or the general API shape.

josepharhar added a commit to josepharhar/html that referenced this issue Sep 18, 2024
The `<selectedoption>` element is part of the customizable `<select>`
proposal: whatwg#9799

It allows authors to declaratively clone the contents of the currently
selected `<option>` of a `<select>` and style it independently for use
in a base appearance `<select>`'s button.

The timing of cloning has been discussed here:
whatwg#10520

The selectedoption element has been discussed generally here:
w3c/csswg-drafts#10242
@josepharhar
Copy link
Contributor Author

whatwg/html#10633

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

No branches or pull requests

5 participants