You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Timing of cloning for the <selectedoption> element #10520
The <selectedoption> element is discussed more generally here, and this issue is for a topic which has been split out: w3c/csswg-drafts#10242
When the author modifies content inside an <option> element, we will clone the contents of that <option> into the <selectedoption>, but the timing at which we do this cloning has not yet been decided.
The timing will be web observable. For example, if the author modifies an option and then tries to query the layout of <selectedoption>, they will have to wait until the cloning happens.
MutationObserver kind of uses microtask timing, but it uses a very special sort, with the compound microtasks. I wonder if the difference will be observable. I think it will, as it will set the "mutation observer microtask queued" agent-wide flag, which will impact the behavior of other web developer-created MutationObservers.
To @domenic 's point, I wonder whether we should use a special type of observer with special microtask timing to minimize observable differences in mutation observers created by web developers. The "clone into selectedoption" microtask could be queued after the regular mutation observer microtask, so that web devs don't see differences in the notify mutation observers step.
The downside is that approach does introduce more complexity, both conceptually and implementation wise. But that might be safer from a web compat perspective.
The over-cloning would happen only if one mutates the content of the selected option, no? The normal case is that user selects one option and the contents get cloned once. So CEReaction or even more synchronous cloning might not be too bad in this case.
Microtasks were designed for MutationObserver, and the reason was to improve performance in cases when one does lots of DOM mutation all over the place. That is not quite the case here.
svg:use, at least in Gecko, is re-cloned when needed when layout is flushed, but of course we don't want to expose layout flush timing.
The over-cloning would happen only if one mutates the content of the selected option, no?
I think cloning would also need to happen if the select element APIs, ex. setting selectedIndex, are used to change the selected option. Authors can update the selected option this way several times before rendering happens.
MutationObserver kind of uses microtask timing, but it uses a very special sort, with the compound microtasks. I wonder if the difference will be observable. I think it will, as it will set the "mutation observer microtask queued" agent-wide flag, which will impact the behavior of other web developer-created MutationObservers.
Chromium has already been using a regular MutationObserver to observe changes to child content in option elements, so I don't see how any behavior could be changed by re-purposing this MutationObserver to be used for <selectedoption>:
The fact that Chrome is violating the spec currently by causing web developer mutation observers to fire at times the spec says are not allowed doesn't seem like a good reason to introduce more such violations.
If we created an alternate set of MutationObserver infrastructure for user-agent MutationObservers, would that resolve this issue?
I'm trying to understand what it would mean for us to do CEReactions timing instead of microtask timing as well. Does it mean that all of the DOM algorithms that append nodes, remove nodes, or modify attributes would have a new step at the end of it to run user-agent MutationObservers?
If we created an alternate set of MutationObserver infrastructure for user-agent MutationObservers, would that resolve this issue?
Yes.
I'm trying to understand what it would mean for us to do CEReactions timing instead of microtask timing as well. Does it mean that all of the DOM algorithms that append nodes, remove nodes, or modify attributes would have a new step at the end of it to run user-agent MutationObservers?
I'm not sure how much else of the CE reactions infrastructure we'd want to import... you'd probably need some sort of microtask timing backup for cases like contenteditable, similar to the backup element queue.
I'm not sure how much else of the CE reactions infrastructure we'd want to import... you'd probably need some sort of microtask timing backup for cases like contenteditable, similar to the backup element queue.
I see, this sounds tricker than going with microtasks.
I think we should go with microtasks instead of CEReactions for the following reasons:
MutationObservers already use microtasks, so trying to create an alternate type of CEReactions MutationObserver would be harder to spec and harder to implement.
Performance will be better when imperatively building or modifying option elements due to fewer calls to clone all of the options contents into selectedoption elements.
As @dandclarksaid in the call, it will be easier to understand how this works because it matches the author defined API of MutationObserver. I think this also increases the likelihood that this element is polyfillable.
Microtasks were designed for MutationObserver, and the reason was to improve performance in cases when one does lots of DOM mutation all over the place. That is not quite the case here.
This might be true, perhaps authors won't be doing lots of imperative modifications to an option element subtree - although we are enabling all sorts of new use cases with the customizable select proposal. Likewise, perhaps authors won't be needing to synchronously query the state of the updated DOM content in the selectedoption element, and if they need to, they can easily queue a microtask or rAF to do so.
So to be clear the proposal is something like "any mutation to the selected option subtree queues a micro-task to re-clone the subtree, unless one is already queued", presumably?
That seems fair, if we're fine with all the relevant weirdness that it causes. It might indeed be the less-bad option...
So to be clear the proposal is something like "any mutation to the selected option subtree queues a micro-task to re-clone the subtree, unless one is already queued", presumably?
If you wanted the content to be totally different, and wanted the solution to work without JavaScript, each option could contain two divs, and you use CSS to show only one when it's within , and the other when it's in .
That's true. Each approach has its advantages and disadvantages.
The disadvantages of the mirroring approach are:
The number of DOM elements doubles, which also increases the payload size. Worst case. In some cases, a single div could probably be used with some conditional child divs, which could mitigate that.
There would be more "churn" in the DOM. It seems like a sledgehammer approach. This would make it more difficult to do things like attach event listeners to specific elements within the button (work arounds would be to attach the event handlers on the button and use capture if some events don't bubble).
If the developer needs to add any adjustments programmatically on the cloned option, it could throw a wrench into the mirroring process, and it would mean the developer needs to be more vigilant about making those programmatic adjustments every time the user makes a selection.
Advantages of the mirroring approach vs a templating solution
The platform hasn't built a templating solution yet (sigh).
If each option looks/behaves significantly different from every other option, that might be easier to achieve via the mirroring approach.
There may be more pro's and con's to each approach that I'm missing, but those seem the most transparent.
The motivation for this suggestion was to support rich behavior that involved event listeners and custom elements inside the select button, selectedoption, and/or options.
However, given the proposed content model, it really looks like any kind of extra interactivity inside <select> is going to be discouraged, and in particular, custom elements are not allowed inside <select>.
If that's the case, then I think just cloning/replacing on selection is fine and no sync'ing should occur by default. I'm ambivalent about adding resetContent() because it seems like modifying the selected option should be strongly discouraged (when is it necessary?).
I do think that if there is a way to do this, it shouldn't be possible to move or modify the children of <selectedoption> via script since they exist only to be styled.
In general, it seems reasonable for devs to be responsible for updating the DOM in the <selectedoption> in these cases.
Not sync'ing would be very different from the default <select> behavior without using <selectedoption>. It's unclear what the a11y implications of this are, but right now in the Chrome prototype it looks like what's announced is based on select.selectedOptions and the content inside the triggering button is ignored. It's also unclear if the behavior is expected or just not finished.
The end tag parsing cloning will clone when the newly appended option is selected. Since the first option always becomes the selected until others are added, this means that the first option will always be cloned. If another option is parsed and appended which has the selected attribute, then it will cause a second clone.
It has been discussed, also in this issue (#10520 (comment)) and during WHATNOT. I think I'd prefer to avoid it and let user know sooner that there is something in the select.
(I guess autocomplete might also trigger a new clone too)
Reacted by Simon Pieters, Anne van Kesteren and Joey Arhar
Activity
domenic commentedon Jul 25, 2024
MutationObserver kind of uses microtask timing, but it uses a very special sort, with the compound microtasks. I wonder if the difference will be observable. I think it will, as it will set the "mutation observer microtask queued" agent-wide flag, which will impact the behavior of other web developer-created MutationObservers.
sanketj commentedon Jul 26, 2024
+1 to microtask timing to avoid over-cloning
To @domenic 's point, I wonder whether we should use a special type of observer with special microtask timing to minimize observable differences in mutation observers created by web developers. The "clone into
selectedoption" microtask could be queued after the regular mutation observer microtask, so that web devs don't see differences in the notify mutation observers step.The downside is that approach does introduce more complexity, both conceptually and implementation wise. But that might be safer from a web compat perspective.
smaug---- commentedon Jul 29, 2024
The over-cloning would happen only if one mutates the content of the selected option, no? The normal case is that user selects one option and the contents get cloned once. So CEReaction or even more synchronous cloning might not be too bad in this case.
Microtasks were designed for MutationObserver, and the reason was to improve performance in cases when one does lots of DOM mutation all over the place. That is not quite the case here.
svg:use, at least in Gecko, is re-cloned when needed when layout is flushed, but of course we don't want to expose layout flush timing.
sanketj commentedon Jul 29, 2024
I think cloning would also need to happen if the select element APIs, ex. setting
selectedIndex, are used to change the selected option. Authors can update the selected option this way several times before rendering happens.josepharhar commentedon Jul 30, 2024
Chromium has already been using a regular MutationObserver to observe changes to child content in option elements, so I don't see how any behavior could be changed by re-purposing this MutationObserver to be used for
<selectedoption>: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/html/forms/html_option_element.cc;l=58-87;drc=e61a3c997dd7d76fdc7fe44b4f20d210bdbd700eThe prototype uses a synchronous mutation observer right now in chromium, and I think that the performance is quite bad because every dom mutation to any element in the whole tree requires us to start walking the dom tree to figure out if we need to update an option/selectedoption: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/html/forms/html_selected_option_element.cc;l=27-32;drc=11e2d7ffa05a985a084057676f9c76c6e88018fa
Using CEReaction sounds much better than synchronous, but I'm still inclined to just reuse MutationObserver the way it is.
domenic commentedon Jul 31, 2024
The fact that Chrome is violating the spec currently by causing web developer mutation observers to fire at times the spec says are not allowed doesn't seem like a good reason to introduce more such violations.
josepharhar commentedon Jul 31, 2024
If we created an alternate set of MutationObserver infrastructure for user-agent MutationObservers, would that resolve this issue?
I'm trying to understand what it would mean for us to do CEReactions timing instead of microtask timing as well. Does it mean that all of the DOM algorithms that append nodes, remove nodes, or modify attributes would have a new step at the end of it to run user-agent MutationObservers?
domenic commentedon Jul 31, 2024
Yes.
In terms of observable effects, yes.
In terms of how you would implement it, I think you'd add a step wherever https://html.spec.whatwg.org/#invoke-custom-element-reactions is called. (Click on the definition to see all call sites.)
I'm not sure how much else of the CE reactions infrastructure we'd want to import... you'd probably need some sort of microtask timing backup for cases like contenteditable, similar to the backup element queue.
josepharhar commentedon Aug 2, 2024
Thanks! I will draft something to create an alternate set of MutationObserver infrastructure in the DOM spec.
I see, this sounds tricker than going with microtasks.
I think we should go with microtasks instead of CEReactions for the following reasons:
This might be true, perhaps authors won't be doing lots of imperative modifications to an option element subtree - although we are enabling all sorts of new use cases with the customizable select proposal. Likewise, perhaps authors won't be needing to synchronously query the state of the updated DOM content in the selectedoption element, and if they need to, they can easily queue a microtask or rAF to do so.
emilio commentedon Aug 5, 2024
So to be clear the proposal is something like "any mutation to the selected option subtree queues a micro-task to re-clone the subtree, unless one is already queued", presumably?
That seems fair, if we're fine with all the relevant weirdness that it causes. It might indeed be the less-bad option...
josepharhar commentedon Aug 5, 2024
Yes, that sounds correct
Improve <selectedoption> performance
Improve <selectedoption> performance
Improve <selectedoption> performance
Improve <selectedoption> performance
48 remaining items
bahrus commentedon Oct 20, 2024
That's true. Each approach has its advantages and disadvantages.
The disadvantages of the mirroring approach are:
Advantages of the mirroring approach vs a templating solution
There may be more pro's and con's to each approach that I'm missing, but those seem the most transparent.
sorvell commentedon Oct 20, 2024
The motivation for this suggestion was to support rich behavior that involved event listeners and custom elements inside the select button, selectedoption, and/or options.
However, given the proposed content model, it really looks like any kind of extra interactivity inside
<select>is going to be discouraged, and in particular, custom elements are not allowed inside<select>.If that's the case, then I think just cloning/replacing on selection is fine and no sync'ing should occur by default. I'm ambivalent about adding
resetContent()because it seems like modifying the selected option should be strongly discouraged (when is it necessary?).I do think that if there is a way to do this, it shouldn't be possible to move or modify the children of
<selectedoption>via script since they exist only to be styled.jakearchibald commentedon Oct 21, 2024
Some examples are given here https://jakearchibald.com/2024/how-should-selectedoption-work/#but-what-if-the-selected-option-is-modified
sorvell commentedon Oct 21, 2024
In general, it seems reasonable for devs to be responsible for updating the DOM in the
<selectedoption>in these cases.Not sync'ing would be very different from the default
<select>behavior without using<selectedoption>. It's unclear what the a11y implications of this are, but right now in the Chrome prototype it looks like what's announced is based onselect.selectedOptionsand the content inside the triggering button is ignored. It's also unclear if the behavior is expected or just not finished.josepharhar commentedon Oct 28, 2024
We discussed this in openui and resolved on not observing/responding to mutations within the option element: openui/open-ui#825 (comment)
I will update the PR to incorporate this.
emilio commentedon Nov 4, 2024
We still need to account for the initial parse somehow, tho, right?
E.g. a large option with the parser yielding in the middle should probably still be cloned...
jakearchibald commentedon Nov 4, 2024
I thought the resolution there was that cloning wouldn't happen until the end tag was parsed?
josepharhar commentedon Nov 4, 2024
Jake is correct. I'm not currently planning on adding any behavior which specifically accounts for or hooks into the yielding behavior of the parser.
I added a step to clone the option into selectedoption (now called selectedcontent) at option end tag parsing here: #10633
The cloning will only occur if the newly appended option element is the selected option in a non-multiple select.
This behavior is implemented in chromium: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/html/parser/html_tree_builder.cc;l=2097-2112;drc=d9b7d6fc39b8525c84f5e97854d966f304291419
josepharhar commentedon Nov 4, 2024
The end tag parsing cloning will clone when the newly appended option is selected. Since the first option always becomes the selected until others are added, this means that the first option will always be cloned. If another option is parsed and appended which has the selected attribute, then it will cause a second clone.
zcorpan commentedon Nov 5, 2024
I guess there are two options for the parsing case:
optionis poppedselectis poppedThe first is better for incremental rendering, but will clone twice if there's an
<option selected>.The second will only do one clone, but there won't be any clone until the whole
selectelement has been parsed.Has this second option been considered and rejected, or not discussed?
smaug---- commentedon Nov 5, 2024
It has been discussed, also in this issue (#10520 (comment)) and during WHATNOT. I think I'd prefer to avoid it and let user know sooner that there is something in the select.
(I guess autocomplete might also trigger a new clone too)
Define customizable <select>
Bug 1911670 [wpt PR 47467] - Improve <selectedoption> performance, a=…