remix logo

Hacker Remix

How should the new <selectedoption> element work?

64 points by jaffathecake 5 days ago | 48 comments

gabesullice 1 day ago

IMO, this whole cloning idea is overcomplicated. Let the duplication of markup happen server side or in React (or whatever).

If the developer wants a <selectedoption>, they should fill it with <option> elements that correspond to the <option> elements that appear in the dropdown. Each option could reuse the <label for=""> pattern to associate them with their counterpart.

Then, when an option is selected by the end user, the corresponding option inside the <selectedoption> would get a `selected` attribute.

The default CSS would be:

selectedoption > option { display: none; } selectedoption > option[selected] { display: block; }

This gives complete control to the developer, works fine without JS, and wouldn't have shocking side effects.

As they say, "duplication is cheaper than the wrong abstraction".

2freedi 1 day ago

Correct if I'm wrong, but this still doesn't allow us to define the dropdown content and selected content for each option separately? Maybe something like this:

    <select name="commenters">
        <option value="annevk">Anne van Kesteren</option>
        <option value="jakearchibald">
            <label>Jake Archibald</label>
            <selectedcontent>
                <div>
                    <img src="profile.avif" alt="Jake's photo">
                    <div>Jake Archibald<br><small>@jakearchibald</small></div>
                </div>
            </selectedcontent>
        </option>
        <option value="sorvell">Steve Orvell</option>
    </select>

The value attribute would be required when using these new sub elements. This structure feels familiar and progressive to me.

Naively, I would imagine that the following JavaScript would cause 1 DOM update, 1 redraw of the option if the dropdown is open, and 1 redraw of the selected option:

    document.querySelector('option:selected selectedcontent').innerHTML = 'Jake Archibald';
Obviously, things are different when using multiple. Maybe a `select > selectedcontent` element containing a copy of each `option > selectedcontent` element that is updated on change events.

jaffathecake 1 day ago

> Correct if I'm wrong, but this still doesn't allow us to define the dropdown content and selected content for each option separately?

There are a few different cases worth considering:

# I want to display the option content in the <selectedoption> as is

Great! Use <selectedoption>

# I want to display the option content in the <selectedoption> a little differently (eg, hide the associated <img> or a different font-weight)

In this case, you're probably best off using a little CSS to style the content of <selectedoption> different, eg hide particular elements.

# I want to display something completely different in the <selectedoption>

<selectedoption> is optional, so one thing you can do here is just… not use it, and instead change the content of your <button> in response to state changes. This requires JavaScript.

If you really want to use <selectedoption> for this, then you could do this:

    <option>
      <div class="option-content">…</div>
      <div class="selectedoption-content">…</div>
    </option>
Along with the CSS:

    option .selectedoption-content { display: none }
    selectedoption .option-content { display: none }

mminer237 23 hours ago

Why should styling even have a separate element? And duplicated? I thought the whole point of CSS was to not clutter HTML with styling.

This shouldn't be a literal new element. It should be a psuedo-element like other component parts like `::-[engine]-slider-thumb` are. You can then style `select::selected-option` and not have to mangle your HTML or worry about mirroring HTML.

jaffathecake 22 hours ago

The issue is that the "selected <option>" and "the <selectedoption> in the <button>" need to be able to render at the same time, behave independently, and be independently styled.

If you have a psuedo-element, it still needs to get its content from somewhere, and needs to be placed somewhere in the tree. With <selectedoption> you can put it wherever you want in the <button>, but you'd lose that functionality with a pseudo. The same questions remain about when the content of the pseudo gets updated. And now you need to figure out how events work.

mminer237 22 hours ago

I agree the question of events still kind of remains. I think they should always be mirrored because they shouldn't be two separate elements. You shouldn't be able to change the "selectedoption" because it should be a pseudo-element. If the <option> changes, the pseudo-element should immediately change. It gets its content from the option.

You don't need to rearrange the HTML of the "button". You can use ::before, ::after, and general styling to do anything you need to. I'm struggling to see any advantage even if you were okay with using HTML to style pages.

jaffathecake 22 hours ago

> I think they should always be mirrored because they shouldn't be two separate elements

That maintains one of the problems I'm worried about. If you have a mouseenter listener on something in an <option> that triggers a fancy animation, you (surely most of the time) don't want that action repeated in the <selectedoption>, because it's the thing in the <option> that was hovered, not the thing in the <selectedoption>.

> You can use ::before, ::after, and general styling to do anything you need to.

You don't need to look at too many web sites to see examples where folks have added divs for styling, because pseudo-elements didn't provide enough style hooks.

mminer237 21 hours ago

You can always add <div>s to the <option> itself and conditionally check whether it's `select > option` or `select::selected-option`.

lifthrasiir 1 day ago

I think it is worthwhile to revisit the starting motivation of customizing `<select>`. There would be generally two camps:

1. Declarative camp, where people expect two differently stylable copies of `<option>` somewhere in `<select>`. I don't think the exact copying mechanism and timing is very important here, as long as it can be safely done and works without JS.

2. Programmable camp, where people want the full control over appearance and behavior. We can reasonably assume that JS is required for this camp, and they generally have better understanding of what gets rendered or not in the first place.

Given both requirements, it seems wise to couple the clone timing with the event processing, which most cases in the second camp would have to do anyway. In the other words, the `input` event would have the default behavior of full cloning, and that default behavior should be prevented via `e.preventDefault()`. The second camp will almost always use this option and can use their own `MutationObserver` or similar to implement edge cases described in the OP. The timing and extent for the default behavior can be then optimized without affecting the second camp. It is even possible to distinguish two camps via different values of the `appearance` CSS property.

Sidetrack: It seems that `<selectedoption>` indeed exists mainly to distinguish two camps? But that leaves many edge cases like multiple `<selectedoption>` elements I believe. Maybe something like `<select reflect="foo"><button><option id="foo">...</option></button>...</select>` might be more appropriate. (`<output>` was my initial thought, but `<option>` would be more regular since it can't nest.)

jaffathecake 1 day ago

> the `input` event would have the default behavior of full cloning

Hm, it feels like, when the selected option is changed via user interaction, the <selectedoption> should be populated before the event fires.

But this is really besides the point, because not all selected-option-changes come with an input event, eg programatic change, including to the content of the currently selected option, which the post focuses on.

> that leaves many edge cases like multiple `<selectedoption>` elements I believe

That isn't really a big problem. It's something the platform already deals with all over the place, like multiple <title> elements, multiple elements with the same ID etc etc.