-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
Allow slotting indirect children #10273
Comments
cc @whatwg/components |
The questions from @hayatoito in #3534 (comment) would need to be answered. Several look serious, such as event path computation, nested shadow hosts, etc. |
For the event path:
Assuming C is clicked, It feels like there are two choices for the event path: The path goes into the shadow DOM after the slotted element, then returns to the light DOM:
The path goes up to a host's child before going into the shadow DOM:
I guess my second option is the 'naive' one. I get the point above, but I'm not sure why this is bad in practice. Is the first option better? I suppose it has the same 'problem' but it's |
I think this is fine with
You get a different result if you change the order. I guess this is why it doesn't work in a declarative system, since there isn't a defined order, but with For completeness:
|
Does this hit the performance issue? |
@rniwa you've looked at this in the past. How do you feel about the 'answers' to the questions above? Any no-gos? |
@mfreed7 are the above comments reasonable answers to the questions? |
I'm not sure. All of them are tractable and implementable (I think) but all are "weird" in some way. On the event path question, both paths are weird in some way. I think I prefer the first one (path goes into the shadow root first, then magically comes back out at the parent of the slotted element) but I'm not sure. Both are pretty hard to follow and understand, for developers. For the assignment of relatives:
Sure, that's great, but that's the easy case since each call to
So you're proposing to allow a child node to be slotted independently of its parent node? That feels like an implementation nightmare, since for every tree walk you'll need to check whether the walked node (at arbitrary depth) is slotted into some slot somewhere. It's unclear whether that condition is that the node is slotted, or just shows up in the manually assigned nodes list. For the tree examples, this whole scenario scares me:
There are so many things going on there. But the weirdest of them is still that D & E are in one slot, but E's child F is in another slot. I think many of these problems might resolve themselves when a prototype implementation is built. It's a bit hard to think about all of the corner cases without actually doing an implementation. I'm supportive of trying to solve the use case, since I think it'd definitely be helpful to be able to slot non-direct children of the host. I'm just worried about all of the grandfather paradoxes. |
@mfreed7 thanks for taking the time to think this through.
So:
Calling
This raises questions about the event path if D is clicked. Does it go
Is it really that crazy? <div id="a">
<div id="b">
<div id="c">
<div id="d"></div>
</div>
</div>
</div>
<style>
#b { --slot: foo; }
#d { --slot: bar; }
</style> Given the above, I can easily query the 'slot' value for any element in that tree using
Maybe we could scope this down. Eg "If an ancestor and child are slotted, the child slotting is ignored (parentmost wins)" The use-cases I can think of can still be solved with those restrictions. And, those restrictions could be removed in future, if use-cases appear. |
No problem. Again, I can see the desired use case, I'm just concerned about the consequences.
Yes, these are exactly my questions. It feels very odd to have the same element occur an arbitrary number of times in the event path. (I don't think it's possible to get a loop, but perhaps I haven't thought creatively enough.) I wonder if it would be easier to just bar this type of assignment? I.e. if you do
Yes, that's true, but CSS selector matchers are built around the cascade, while DOM code is not. This issue represents the changing of an invariant that is baked in to many parts of current DOM code to deal with shadow roots.
Ahh maybe this answers my question above. I do think it would dramatically simplify things. I like your way out - parentmost wins. That feels clean from a developer's point of view, but the implementation still has questions. Like "when" does the "winning" happen. When you call |
Fair. I don't have a feeling for how much work this would be. Does #5033 (comment) represent a similar problem, where the heading level is incremented by multiple ancestor elements?
Yeah, I agree that the latter feels better, DX wise. |
I haven't thought a ton about it, but my knee jerk reaction is that the heading level incrementer is quite a bit more localized and simple. It'll just require an
Kind of has to be, at least to stay true to how |
When I read this, I thought, is something like element() planed? Is there a draft spec for it? (I wanted smth. like this for years) |
i know this, but is there a spec? |
@jogibear9988 for future reference, MDN pages tend to link to specs https://developer.mozilla.org/en-US/docs/Web/CSS/element#specifications. In fact, I often find this the quickest way to find a spec for a particular feature. |
It'd be great to describe more use cases where this feature would be helpful. For some things just the ability to style elements in the slotted subtree (as you could do in the v0 spec with For the custom select element described in this issue, it's unclear if non-child slotting would really be necessary. The approach being taken for the styleable select element seems to be different. There, a |
It might be worth checking whether that design was influenced by the current limitations. Another example would be a data table: <ui-table>
<ui-table-row>
<ui-table-cell>…</ui-table-cell>
…
</ui-table-row>
…
</ui-table> There the output would be a filtered and sorted selection of the data presented, but the cell data would be displayed as-is, allowing for rich content, events, etc. |
How does the need for duplication of data relate to slotting indirect children? (A number of alternatives were considered by the CSS WG/WHATWG, and (unfortunately) cloning + insertion is the best we have, but it's not related to limitations around slotting.) |
If I wanted to implement something like a custom select whilst avoiding content duplication, I'd either:
In either case, I think I'd need to be able to slot indirect children. Maybe the table example is better if the select example is a distraction. |
Regarding the select example, it's probably good to distinguish conceptually slotting as a logical concern from as a rendering one. However, I don't think this has much pragmatic value since, for example, For the table example, it does seem like selecting the rows is "correct" and what's needed for proper rendering is more control over styling the subtree where the row would typically be Here are a couple of other examples where this feature might be useful.
<tab-group>
#shadowRoot
<div class="header"><slot name="header"></slot></div>
<div class="content"><slot></slot></div>
<tab-panel>
<tab-header>tab 1 header</tab-header>
tab 1 content...
</tab-panel>
</tab-group>
<editable-element>
content...
<edit-controls>...</edit-controls>
</editable-element>
...
<element-editor></element-editor> |
That's not how 99.9% of select's work today, and I think it'd cause user confusion.
This is also not how ~80% of select's work today, in that the styling is different for the selected option in the page (e.g. smaller images, different fonts, hidden parts) vs. the list of options.
I agree - |
Another use-case would be an enhanced datalist: <ui-datalist>
<ui-datalist-group>
<ui-group-summary>England</ui-group-summary>
<ui-datalist-option>London <small>(capital)</small></ui-datalist-option>
<ui-datalist-option>Brighton</ui-datalist-option>
…
</ui-datalist-group>
<ui-datalist-group>
<ui-group-summary>Scotland</ui-group-summary>
<ui-datalist-option>Edinburgh <small>(capital)</small></ui-datalist-option>
<ui-datalist-option>Glasgow</ui-datalist-option>
</ui-datalist-group>
</ui-datalist> Typing in an input would display a filtered version of the input data. Groups would be omitted if no items within matched. To do this today, I'd have to modify the |
What problem are you trying to solve?
Imagine a custom element for a custom
<select>
:Sometimes the options will be rendered in their groups in an overlay. Sometimes the selected option will be rendered inline.
Implementation-wise, this would be two slots:
When the select is in the closed state, I want to be able to assign the
childNodes
of the selected<ui-select-option>
to the "selected item content" slot.When the select is in the open state, I want to unassign the nodes in the "selected item content" slot, and assign the
<ui-select>
'schildNodes
to the "overlay content" slot (I may use something likeelement()
to make the selected item appear to be in both places at once).What solutions exist today?
slot.assign(...nodes)
However, the nodes have to be direct children of the shadow host, so it doesn't work for the use-case above.
How would you solve it?
Allow slottables to be descendants of the shadow root host.
This could be allowed for
slot.assign
only, or it could be allowed for slottables in general.I don't know if this would need to be an opt-in behaviour. Right now, surprisingly, calling
slot.assign
on a node that isn't a direct child of the shadow host, fails silently. Well, technically it succeeds, but the set nodes are ignored in step 5.2 of find slottables.Anything else?
There are other cases where this would be useful. Eg, cases where the light-dom content of a custom element is a mix of data and content.
For example, the light-dom content could describe a list of books. The shadow DOM may render this as a table, or a list, potentially sorted, depending on the environment and interactions. Again, in this case, the component would want to manually slot the content.
Previous discussions:
The text was updated successfully, but these errors were encountered: