Back

How should the new <selectedoption> element work?

11 points4 daysjakearchibald.com
dfabulich4 days ago

I think it should be Option 2, "Automatically reset the content when anything in the selected <option> changes."

But furthermore, I think it should be possible to turn <selectedoption> mirroring entirely off, e.g. with an attribute like <selectedoption mirroring="none">, and I think most developers using reactive frameworks should prefer to do that.

If I'm using any reactive framework designed to do targeted DOM updates, I want my framework to be in complete control. When an option is selected, I'll populate <selectedoption> myself. When an <option> is modified, I modified it, and I know which <option> is selected, so I'll perform a targeted DOM update of <selectedoption> as well.

You had a whole separate podcast episode about how/why having the browser itself do targeted DOM updates is an enormous can of worms, mostly for attribute vs. property reasons. https://offthemainthread.tech/episode/putting-react-in-the-b... And anyway, every framework wants to handle mutations differently, and framework designers are in consensus that we haven't picked a clear winner yet. So, as nice as it would be if we had a replaceHTML API that everybody loved, we don't have that now, and we shouldn't hold up customizable <select> for this.

<selectedoption> mirroring is for folks who don't want to use any JavaScript (and I think that's a good thing). In that case, mirroring all updates automatically, synchronously, is the simplest thing that can possibly work.

Developers who want optimal performance want full control, and they just want to turn mirroring entirely off.

jaffathecake4 days ago

Thanks for the feedback! Fwiw, there's no point populating <selectedoption> yourself. Just don't use <selectedoption>. It's optional. That's probably what I'd do in most cases where I'm using a framework.

dfabulich4 days ago

Good to know.

Say, is there a bug in Chrome Canary 132? When I opt into customizable <select>, "change" events stop firing.

    <style> select, ::picker(select) { appearance: base-select; } </style>
    <select>
    <option value="one">one</option>
    <option value="two">two</option>
    <option value="three">three</option>
    </select>
    <script>
    document.querySelector('select').addEventListener('change', e => {
        console.log(e);
    })
    </script>
And another. When I add <selectedoption>, I get a warning in the console, "A descendant of a <select> does not follow the content model. <selectedoption> one </selectedoption> "

    <style> select, ::picker(select) { appearance: base-select; } </style>
    <select>
    <button><selectedoption></selectedoption></button>
    <option value="one">one</option>
    <option value="two">two</option>
    <option value="three">three</option>
    </select>
jaffathecake4 days ago

Those sound like bugs but I'm not really familiar with the implementation. I haven't played with it much either. I'm just an outsider interested in the feature design.

TheFlyingFish3 days ago

My feeling is that the most important situation to consider here is where the updating happens from third-party JS. If you're writing your own JS, it isn't that much harder to just target the selectedoption as well as the original one, and if you're using a full-fat framework like React or whatever it's downright easy. So I think the benefit of providing an explicit API to trigger a clone is limited.

So that leaves the various automatic options, either synchronous, debounced, or the fancy targeted version. This seems like a pretty straightforward complexity/performance tradeoff to me, with the synchronous version being the simplest (both to implement and to understand) and going up from there.

With that in mind, I'm inclined toward the middle option (changes are synced automatically, but batched in the next microtask) since it feels like the best balance of complexity/usability. Seems like it would eliminate some of the biggest performance footguns, without being too much of a bear to implement or too difficult to understand.

On the other hand, I would personally also be ok with no syncing at all, just the initial clone when an option is selected, if it would mean we get this feature sooner. Really looking forward to not having to roll my own dropdowns.

butchler3 days ago

If you're using a framework like React to manage updates to the DOM, then you probably don't want to use selectedoption in the first place because it's just as easy and more flexible/predictable to use the framework's existing patterns for keeping different parts of the DOM in sync.

So the main target audience for selectedoption is probably people not using a framework, and using no or little JavaScript. That audience probably would want selectedoption to "just work" without having to manually call a function to reset things. So while option 1 seems like a reasonable option to me personally as someone who mainly works with React, it's probably not what most people using selectedoption would want.

Option 2 just has too much potential for introducing hard to debug and hard to fix performance issues, so I feel like it's automatically not the best choice. Its main advantage is that it's probably the most straightforward to understand/explain.

Option 4 would help with the performance issues, but it sounds like it would be a nightmare for browsers to implement. It would also be a bit difficult to explain to devs how exactly it works and what edge cases need to be considered (like mutating the selectedoption "fork" potentially causing issues).

So by process of elimination option 3 seems like the best to me. It solves the performance issues but it's still pretty easy to understand/explain. It's main disadvantage seems to be that from the perspective of code running synchronously it's possible for the selectedoption and option to get out of sync, but 1) it's a bit hard to imagine a practical use case for needing to do this kind of comparison and 2) simply waiting until the next microtask before doing the comparison would probably be an easy fix even if this is a problem in some cases.

For all of these options, it might be useful for there to be some way to intercept the default behavior. For example maybe there could be some event such that calling .preventDefault() inside an event handler has the side effect of preventing the selectedoption from being reset, if resetting it is not desired. Of course you don't need to use selectedoption at all if the automatic resetting is not desired, but maybe there will be some cases where you only want to make an exception in a few cases but generally do want the selectedoption default behavior.