Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.Sign up
Imperative shadow DOM distribution API #3534
We should add a
How does this interact with the existing declarative distribution API? Maybe it just throws if the
This API is not "perfect" because authors don't have enough hooks to call it as often as you might want. E.g. if you are trying to emulate details/summary, you will use a MutationObserver to watch for child node changes and do
But, it's pretty darn good!! I'm really excited about not putting
Any further thoughts?
Thanks for filing a new issue. Yup. I think that is feasible. Let me explore API design and its semantics deeper.
I am also wondering whether there is a use case which imperative API can't address, or not. Please let us know if there is. I hope this kind of imperative API can address the rest of the world.
That wouldn't address the elements potentially getting slotted elsewhere.
This will also require some careful study of the mutation algorithms (a fair number of which simply reset the assigned nodes).
Thanks. I am afraid I am not enough of a shadow DOM/DOM spec expert to do that careful study. But, I will try to coordinate folks into answering the question of
Hopefully if we can answer "yes", then this would be high-priority enough for you experts to help us write the spec?
One thing we should decide is how declarative APIs and imperative APIs interact each other. Mixing them is troublesome and would be the cause of a confusion.
One idea to make the situation much simpler is to get "opt-in" from web developers to allow them to use imperative APIs. The scope of 'opt-in' should be a shadow tree.
In other words, declarative APIs and imperative APIs should be mutually exclusive in one shadow tree.
added a commit
Mar 8, 2018
I've posted my straw-man proposal here.
I hope this can capture most use cases, with minimum changes to DOM Standard, HTML Standard, and browser's engines.
They are never used in other shadow trees. Let me show an example.
slot2.assign([A]); assert(slot2.assignedNodes() == ); slot1.assign([A]); assert(slot1.assignedNodes() == [A]); shadowroot2.append(slot1); assert(slot1.assignedNodes() == ); shadowroot1.append(slot1); assert(slot1.assignedNodes() == [A]);
I didn't introduce any restriction to manually-assigned-nodes. Any programmer's mistake can be okay there by design. Invalid nodes in manually-assigned-nodes are never selected as assigned nodes. assigned nodes are only observable.
I think this design choice would make the standard and the implementation much simpler.
Anyway, let me state that clearly.
Any alternative ideas are welcome, of course. I would like to hear feedback.
That won't quite work if we allowed
Consider when the case when (x) is manually assigned of (d). In that case, (d) would belong to both (x) and (y), which shouldn't be allowed.
I think a lot simpler model is to keep track of the slot to which a given node is manually assigned, and forbid declarative slotting from picking that node.
Namely, each node will have an internal slot manually slotted flag, which is initially set to
I think there is misunderstanding. Even if
Even if the same node is added to
See Example 3, where
referenced this issue
Mar 9, 2018
@hayatoito, the proposal is looking good from the perspective of doing the manual allocation, but it is not providing enough information, or end-to-end examples.
My main concern is that with
I think from our side, we will like to have a reliable signal for changes on the light tree so the allocation can happen accordingly. The most dummy example could be:
<fancy-menu> <menu-item>one</menu-item> <menu-item>two</menu-item> </fancy-menu>
When adding a new menu item (e.g.:
I can envision scenarios in which a developer would like to mix slotting by name and/or default slotting with manual slotting.
Suppose an app has a menu that shows menu items, some of which are conditionally available. Perhaps it supports markup like:
<menu-element> <div slot="caption">Menu</div> <menu-item show-when="signedout">Create account</menu-item> <menu-item show-when="signedin">Account settings</menu-item> <menu-item>Help</menu-item> </menu-element>
<template> <slot name="caption"></slot> <slot name="availableItems"></slot> </template>
This menu element wants to leverage preexisting support for slotting by name — in this situation, slotting a caption into the
Could the proposal be extended to allow, when
Along the same lines, I could imagine wanting to have a default slot still serve as the default destination even when manual slotting is being used for specific slots. It would be useful for a component to manually pluck the nodes it wants to assign to specific slots, then let everything else fall into the default (unnamed) slot.
In short, rather than having
[Note: the existing text of step 6 above fails to mention the default slot, but probably should.]
If such an accommodation could be found, it might allow the introduction of an imperative slotting API without needing to introduce a
(Side note: If it'd be helpful to fork this topic into a separate issue, I can do that. Perhaps someone who can create labels on this repo could set up a label for imperative distribution?)
Thanks for the feedback. I don't have enough bandwidth to reply all today, so let me reply in the next week.
[Update: I understood that the following explanation is not directly related to @caridy's concern, but let me keep the following explanation, as a side note, because the concept of manually-assigned-nodes is likely to cause a confusion to end-users.]
We still auto-calculate assigned nodes for each slot in a shadow tree, using the information of manually-assigned-nodes users gave for each slot. manually-assigned-nodes is just a hint for an engine. The engine can't trust manually-assigned-nodes as is because manually-assigned-nodes may include an invalid node which can't be used as a member of assigned nodes.
Okay, then your proposal doesn't satisfy a major use case of the imperative API to select a non-child node of a shadow host. I don't think that's okay. An imperative API should allow slotting of an arbitrarily deep descendent node.
It would help to have some concrete examples of elements that want to slot a deep descendant. I'm unsure if select/optgroup/option would count here (i.e., would select slot both options and optgroups, or would it slot its children, and then optgroup slots its children).
Nevertheless, I agree it seems like something we should be aiming for, especially if we want to fulfill
See w3c/webcomponents#574 for a concrete example.
Just to be clear, we'd be opposed to any imperative API proposal which doesn't support this use case since we see this as one of the primary motivations for having imperative API at all. In fact, the only reason we receded our proposal to support this in declarative syntax was one of Google representatives made an argument that we can support this in imperative API.
referenced this issue
Mar 9, 2018
Regarding w3c/webcomponents#574 (slotting indirect children),
I remember w3c/webcomponents#574, where I pointed out why "slotting indirect children" is a non-starter from the theoretical and practical perspective, however, I didn't get any response on that. It looks that only I have explored this problem space so far.
To avoid answering the same question repeatedly, it would be better to share my insights clearly here. Please read these and think carefully by yourself before filing a request for "slotting indirect children".
First of all, supporting "slotting indirect children" is NOT a nice-to-have feature. It is a sort of an attempt to introduce "division-by-zero" to the beauty of the Shadow DOM world. That is infeasible.
In other words, "Only host's children can be distributed" is MANDATORY. That has been the fundamental requirement to make Shadow DOM work, from the very early days of Shadow DOM.
For those who will explore this problem from now, let me share several things which you will encounter, hoping this would be helpful to understand what problems you are trying to solve:
1. Think your proposal's impact on the event path.
Think the following simple case.
And think how you can update get the parent algorithm so that weird things shouldn't happen, such as:
2. Think how your proposal work with: Two nodes are assigned to the same slot (or different slots) in the same shadow tree, however, one node is an ancestor of the other.
3. Think your proposal's impact on a nested web components case
The flat tree would be:
Think what happens: attach shadow to B (and append slot2 to B' shadow root)
Think about more complex scenerio and how your concrete proposal can have a reasonable answer for that, from the performance's perspective. e.g.. to avoid O(n) traversing.
4.Think the use case
The use case in w3c/webcomponents#574 didn't make sense to me. I pointed out, "Remove unnecessary
Only remaining appealing point for that seems to me:
However, in general, DOM doesn't allow inserting such a "no-op" comment container node in the markup.
And, I've never heard such a weird requirement from actual users of Web Components, such as Polymer team.
I have showed a couple of examples here, however, I am pretty sure these are not only things which would be broken. So please try to have a concrete proposal, instead of just replying to each example. Please don't let me guess how your proposal would be. That would be unproductive for us.
I am happy to review a proposal if someone has explored this problem space deeply and still can have a concrete proposal which can support "slotting indirect children".
The assertion that not being able to assign a non-direct child to a slot is a requirement for shadow DOM is utterly false.
In fact, I've explored this problem space and prototyped such a model. I can post a detailed proposal later (not possible in the next few weeks or months due to other commiments) but I figured you can sort it out yourself; I gusss not.
My earlier comment withstands. Any proposal for an imperative slotting API that doesn't support assigning a non-direct child is a show stopper for Apple. It was a show stopper four years ago, and it is a show stopper today.
I'm quite surprised that we're having this conversation again because I felt like we made our position very clear then. It's one issue we can't compromise.
Since @rniwa is not able to help us for the next few months, I am wondering if it's worth moving forward with a child-only version, that can then in the future be extended to support more descendants when @rniwa has time to help us figure out the model?
I understand it's a must for @rniwa to solve arbitrary descendants. But children are a subset of descendants, so if we can come up with a subset proposal that can be extended in the future, we may be able to make progress instead of stalling for months.
It would be great for whatever solution to be super obvious (explicit) about when your element is rendered to another place besides the immediate parent, and not unexpected.
For this to be true, it may be the mechanism has to be only imperative and reference based. It would be great if only a Custom Element could explicitly do teleporting to some portal by reference, perhaps using a private API (see w3c/webcomponents#758 about giving APIs to CE authors).
<body> <div> <portal></portal> </div> <div> <my-el></my-el> </div> </body>
// Inside MyEl class const portal = document.querySelector('portal') private(this).teleport(portal) // my-el renders relative to where portal is located
(Private class fields are coming soon.)
This idea is not tied to ShadowDOM. But for it to work in ShadowDOM, a component author would place it in a root then expose the portal reference to the outside.
A (grand)child element of the component could traverse up the tree to find the component from which to get the reference from (f.e. in
Outside code cannot get a reference to the
The connection is made purely imperatively (the
(I know this is not a proposal, I'm just throwing in an idea to get cogs turning)