Skip to content
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

Finding a definition for disconnected elements #1040

Open
sorvell opened this issue Dec 8, 2023 · 25 comments
Open

Finding a definition for disconnected elements #1040

sorvell opened this issue Dec 8, 2023 · 25 comments

Comments

@sorvell
Copy link

sorvell commented Dec 8, 2023

Assuming the behavior described in the explainer's Finding a custom element definition section, consider the following.

  1. Define x-foo in the global and a scoped registry.
  2. In a shadowRoot using that registry, create an element:el = shadowRoot.createElement('div').
  3. Now create and x-foo via innerHTML: el.innerHTML = '<x-foo></x-foo>'.

I believe this results in an x-foo element upgraded via the global registry, and that behavior seems surprising and unexpected. I would expect in that case the x-foo element would use the scoped registry definition.

Perhaps this could be addressed if elements had an ownerRegistry setting (similar to ownerDocument) that could be used to lookup the correct definition.

@justinfagnani
Copy link
Contributor

The reason why the proposal doesn't have elements remember their registry is because the Chrome DOM team at the time was very reluctant to adding another reference to all elements to point to the registry due to memory concerns. That changed the design to dynamically looking up the registry by using the element's current root node if the root node is a ShadowRoot.

Maybe there's a pattern where only root node elements created within a scope need to store their registry, and all other elements don't store a registry. I'm not sure if most DOM implementations put some references into a dynamic map (I think Blink calls this "rare data" maybe?), but that could reduce the memory overhead.

Thoughts @mfreed7 @rniwa?

@trusktr
Copy link
Contributor

trusktr commented Apr 28, 2024

I would expect in that case the x-foo element would use the scoped registry definition.

100%

I can't think of how this can be allowable. It's going to throw many people off.

@sorvell
Copy link
Author

sorvell commented May 3, 2024

After discussion at the April F2F.

Implementor consensus seemed to point to a concern over memory use related to the need to track an element's "active registry."

An alternate proposal that seemed more feasible was to add registry to the options for setHTML. For example:

  const d = shadowRoot.createElement('div');
  d.setHTML(`<x-foo></x-foo>`, {registry: shadowRoot.registry});

And effectively this would do the same thing as:

  setHTML = (node, html, registry) => {
    const template = document.createElement('template');
    template.innerHTML = html;
    registry.upgrade(template.content);
    node.replaceChildren(...template.content.childNodes);
  } 

@josepharhar
Copy link

tpac meeting notes:

10:34 <jarhar> justinf: we had 3 open questions last time and we settled all of them
10:35 <jarhar> steve: have a registry on the apis for creating elements that are disconnected 
10:35 <jarhar> steve: sethtml can have an option bag which can specify a registry
10:35 <jarhar> steve: that was resolved to be the simplest at the f2f, and its capture in the issue on this
10:35 <jarhar> masonf: to clarify: the proposal is that innerhtml will use the global registry, but sethtml/sethtmlunsafe will have an additional argument to use a registry
10:36 *** sorvell (~sorvell@9d40a032.publics.cloak) has joined the channel
10:36 <jarhar> sorvell: my sense is that while its not ideal it makes the feature reasonable
10:36 <jarhar> rniwa: sethtml and sethtmlunsafe - we want to add the argument to both of them
10:36 <jarhar> sorvell: ideally yeah
10:36 <masonf> presumably parseHtml and parseHtmlUnsafe() also?
10:37 <jarhar> justinf: related: how to get registry for disconnected tree but also for a library ...
10:37 <jarhar> justinf: they could have similar solutions or different
10:37 <jarhar> sorvell: that is captured. i tried to make a summary of the issues in the breakout session github issue at the bottom
10:37 <jarhar> sorvell: rob made an issue for that and i had a separate issue for that
10:37 <jarhar> sorvell: my recollection at the last f2f was that we had discussed that those could be follow ons as long as we solve this disconnected issues which was needed for an mvp
10:38 <jarhar> sorvell: the two proposals around framework integration were trying to make that more seamless
10:38 <masonf> q?
10:38 * Zakim sees no one on the speaker queue
10:38 <jarhar> sorvell: theres the steps to find the custom element registry. look for a root or use the global
10:38 <jarhar> sorvell: robs idea was that you could make a function wrapper - any time inside this callback, use this registry that im giving you
10:39 <jarhar> sorvell: my proposal was different - you could define a custom element - the problem is that in this disconnected state you can customize if youre in the global registry. you can define an element and only upgrade this element dont customize when its created. it wouldnt customize when its created. it would only customize when its connected to the root that has the registry that you want, which is how upgrades work
10:39 <jarhar> sorvell: both of them are too hard to try to get through to get this landed. both of them could be done as a follow on to do better framework integration. in the meantime they can work around it
10:40 <keithamus> q?
10:40 * Zakim sees no one on the speaker queue
10:40 <keithamus> q+
10:40 * Zakim sees keithamus on the speaker queue
10:40 <jarhar> sorvell: this is the lit communitys most requested thing, i would love to see us get the mvp specified and on the road to impl
10:40 <jarhar> rniwa: could you qualify the problem statement?
10:41 <justinf> q+
10:41 * Zakim sees keithamus, justinf on the speaker queue
10:41 <jarhar> sorvell: react and frameworks dont use special api to create dom, which you will have to use. to get those frameworks to use a new api to create dom it will be hard. to make this as seamless as we can for them to get this stuff for free that would be great. they dont use shadowdom, but you can make a react element or a framework element that stamps into shadowdom
10:41 <jarhar> sorvell: we should do framework integration as a followon
10:42 <astearns> ack keithamus
10:42 * Zakim sees justinf on the speaker queue
10:42 <jarhar> keithamus: i dont think we should be specifying features to work aroudn existing limitations of frameworks. we should write patches for those frameworks or ask them to. the idea of executing a block of code by ... it would create more problems than it solves
10:42 <rniwa> q+
10:42 * Zakim sees justinf, rniwa on the speaker queue
10:42 <jarhar> keithamus: the most ergonomic way to do this is to hang apis off of getrootnode and ask them to use that instead of document
10:43 <jarhar> sorvell: thats a solid argument. that discussion should not block the core feature
10:43 <masonf> ack justin
10:43 * Zakim sees rniwa on the speaker queue
10:44 <jarhar> justinf: i disagree with keith. theres a long history in the web components space of saying this is the new reality and - its taken 10 years for react to support custom elements with attributes and properties. we need to build bridges to those destinations. maybe react will never move away from document.createelement. being able to say im going to call this library and this library calls document.createelement
10:44 <masonf> ack rniwa
10:44 * Zakim sees no one on the speaker queue
10:44 <jarhar> rniwa: this isnt really a framework integration as much as its making the framework use your registry. you want to force it to integrate
10:45 <jarhar> keithamus: force is the word. i respect the argument that frameworks can be very slow to adopt these features, but if we introduce code that forces a situation, we might see the opposite effect where theyre quick to work around it. we're making a hostile situation in their code. the expectations of how their code is being executed is changing because outside is forcing it, and frameworks try to address this, and it will have the opposite effect that we want. 
10:46 <jarhar> justinf: isnt the global registry forcing them to use custom elements? were changing which definitions to use. theyre already unaware of the registry anyway
10:46 <masonf> q+
10:46 * Zakim sees masonf on the speaker queue
10:46 <jarhar> rniwa: lets say were going to do this thing in this function that uses document.createelement. what happens when someone uses innerhtml on it after?
10:46 <jarhar> justinf: if that was attached to the shadowroot then the current steps work. otherwise its an open question, what to do about detached trees
10:46 <justinf> q+
10:46 * Zakim sees masonf, justinf on the speaker queue
10:47 <jarhar> keithamus: its your point around timing. if youre saying that during the lifetime of a function we're using this registry. if they do a single await or settimeout then suddenly we changed the pointer of the scoped registry and you have an intermediate set of
10:47 <jarhar> justinf: the react patch is to patch the owner on prototype and not be a document but use a scoped registry. its sync and fragile but its what you have to do to get react to work
10:47 <jarhar> keithamus: make that a proper part of the spec including the fragility
10:47 <sorvell> q+
10:47 * Zakim sees masonf, justinf, sorvell on the speaker queue
10:48 <rniwa> ack masonf
10:48 * Zakim sees justinf, sorvell on the speaker queue
10:48 <jarhar> masonf: im sympathetic to keith, lets not write an api for frameworks but lets make it good. justins point is a good one, the global registry is already happening and frameworks dont know. a similar api effect would be document.defaultregistry and set it to what you want and then run your code. its similar to the global registry, were just changing how custom elements work 
10:48 <jarhar> keithamus: you can mitigate timing issues
10:49 <jarhar> rniwa: isnt the whole point of scoped registries that you want multiple scoped registries? presumably different parts of the document are trying to use different scoped registries. 
10:49 <masonf> q?
10:49 * Zakim sees justinf, sorvell on the speaker queue
10:49 <jarhar> justinf: its a workaround when youre rendering into a shadowroot but the framework isnt aware of that
10:50 <masonf> ack justin
10:50 * Zakim sees sorvell on the speaker queue
10:50 <jarhar> justinf: it could be used for micro frontned architectures. i have an element defined and i want to use it but i cant because it appears at the top of the document. if theres a way to create an element and force it to use a particular registry that would be useful
10:50 <jarhar> justinf: i want to take a step back. we have two open questions and we have zerod in on one of them. i dont know if people agree that the disconnected trees one is more important
10:50 <jarhar> rniwa: this uses different registry hinges on what we do with a disconnected tree, they are very related
10:50 <jarhar> justinf: how do people feel about that?
10:51 <jarhar> keithamus: if we have the same suite of apis on shadowroots then we avoid the problem of disconnected trees. people will have to use getrootnode
10:51 <jarhar> justinf: the spec does include putting a subset of element creation apis
10:51 <jarhar> justinf: if people started using that
10:51 <jarhar> sorvell: theres still a problem of innerhtml
10:52 <masonf> ack sorvell
10:52 * Zakim sees no one on the speaker queue
10:52 <jarhar> sorvell: the core issue is innerhtmling which shadowroot innerhtml which doesnt work in the prototype yet. sethtml should work at the least
10:52 <justinf> q+
10:52 * Zakim sees justinf on the speaker queue
10:52 <westbrook> Referencing: https://issues.chromium.org/issues/336984024
10:52 <jarhar> sorvell: if we have sethtml and we have a solution that does match this idea of - it is not a framework related solution we need a way of creating dom in a registry on an element
10:52 <jarhar> keithamus: thats where i think having everything on a root node solves that
10:53 <jarhar> sorvell: how does it solve inner html?
10:53 <masonf> ack justinf
10:53 * Zakim sees no one on the speaker queue
10:53 <jarhar> justinf: the lookup registry steps use the global registry. there was concern about the memory overhead of having a pointer on a node
10:53 <jarhar> justinf: i wonder if we can hang a rare data...
10:53 <jarhar> sorvell: at the last f2f the implementors said no
10:54 <jarhar> justinf: the lookup a registry steps look to see if theres a registry on the root node
10:54 <sanketj> q+
10:54 * Zakim sees sanketj on the speaker queue
10:54 <jarhar> keithamus: innerhtml is deprecated, sethtml and gethtml should be used, they have an options bag and you can pass in a registry. does that solve the issue?
10:54 <jarhar> sorvell: it solves the i need to do some inner html on this disconnected node. option bag is on element. if you dont know the registry
10:55 <jarhar> keithamus: if you did shadowroot.createelement, it would be disconnected and wouldnt be inside the shadowroot. if you did sethtml you already have a registry but tractable. is the registry a public part of the shadowroot?
10:55 <jarhar> justinf: does someone know the right registry to pass? if you create an element with a scoped registry and hand it off?
10:55 <jarhar> keithamus: thats always the same problem. you still have to have a reference to the registry at some point
10:56 <jarhar> justinf: if the root node knows its registry
10:56 <jarhar> justinf: for a disconnected tree
10:56 <jarhar> sorvell: it could function like owner document. once you put it in a new document it changes
10:56 <jarhar> sorvell: that would probably be better, but implementors said no already
10:56 <jarhar> sorvell: so this is the next best thing
10:56 <masonf> q?
10:56 * Zakim sees sanketj on the speaker queue
10:56 <masonf> ack sanketj
10:56 <jarhar> sanketj: could you elaborate on the implementors saying no?
10:56 * Zakim sees no one on the speaker queue
10:57 <jarhar> keithamus: elements dont have a pointer to the scoped registry because the default lregistry is going to be the specified one
10:57 <jarhar> keithamus: adding a pointer to every element would be a lot
10:57 <jarhar> rniwa: it would not be rare though
10:58 <jarhar> keithamus: createelement would have to return some sort of boxed value. if its an element then you have a box which says this is the custom element and this is the registry and unpacking it would be a bunch of extra work
10:58 <jarhar> justinf: if you do create element without a registry it would use the global registry. if you do it in a shadowroot then it would set it 
10:58 <jarhar> masonf: if you attached it to a tree it could delete that rare thing
10:58 <jarhar> sorvell: if you disconnect you have to put it back
10:58 <rniwa> q?
10:58 * Zakim sees no one on the speaker queue
10:59 <jarhar> sanketj: i thought steve was saying tha tthe registry should live on the document or root and you have this to do the lookup, and that was the thing that was implementor objected about
10:59 <jarhar> justinf: the objection was to have a new pointer that every element has to have
10:59 <jarhar> sorvell: i think that the sethtml having an arguments bag is enough to move forward on this. its not ideal but i think its a ocmpropose thats reasonable and gets this feature off the ground.
11:00 <jarhar> sorvell: this is incredibly problematic for a lot of use cases, and if we can solve it then its awesome
11:01 <justinf> q+
11:01 * Zakim sees justinf on the speaker queue
11:01 <jarhar> jeff: im one of those people. ?? you lose out on some of those features like innerhtml or createelement. this whole system with registries sounds like its going to be cumbersome and how do i create a library that people can use? i really tried to use native elements instead of frameworks but i still hit that wall. if theres still going to be a wall that im going to hit, especially with slots
11:01 <jarhar> rniwa: we are at time. we need to take the discussion offline
11:02 <jarhar> masonf: can somebody post the issue? i feel like we got close to adding an options bag to setinnerhtml
11:02 <sanketj> https://github.com/WICG/webcomponents/issues/1040
11:03 <sorvell> https://github.com/WICG/webcomponents/issues/1040#issuecomment-2093725017
11:03 <jarhar> justinf: i can create a pr and we can argue about whether to merge it

@rniwa
Copy link
Collaborator

rniwa commented Nov 21, 2024

It's clear that we want to use the scoped custom element registry in the case of setting innerHTML to an element in an element newly created for a scoped custom element registry:

function createConnectedShadowTree(registry) {
    const host = document.createElement('div');
    const shadowRoot = host.attachShadow({mode: 'closed', registry});
    document.body.appendChild(host);
    return shadowRoot;
}

const registry = new CustomElementRegistry;

class SomeElement extends HTMLElement { };
registry.define('some-element', SomeElement);

class OtherElement extends HTMLElement { };
registry.define('other-element', OtherElement);

const shadowRoot = createConnectedShadowTree(registry);
const someElement = shadowRoot.createElement('some-element');
someElement.innerHTML = '<other-element></other-element>';
someElement.querySelector('other-element') instanceof OtherElement; // This should evaluate to true.

But what happens when such an element gets inserted into another shadow tree with a different scoped registry? Or what happens if the same element gets removed from the shadow tree?

class OtherElement1 extends HTMLElement { };
customElements.define('other-element', OtherElement1);

const registry1 = new CustomElementRegistry;
class SomeElement extends HTMLElement { };
registry1.define('some-element', SomeElement);
class OtherElement2 extends HTMLElement { };
registry1.define('other-element', OtherElement2);

const registry2 = new CustomElementRegistry;
class OtherElement3 extends HTMLElement { };
registry2.define('other-element', OtherElement3);

const shadowRoot1 = createConnectedShadowTree(registry1);
const shadowRoot2 = createConnectedShadowTree(registry2);
const someElement = shadowRoot1.createElement('some-element');
someElement.innerHTML = '<other-element></other-element>';
someElement.querySelector('other-element') instanceof OtherElement2; // This should evaluate to true.
shadowRoot2.appendChild(someElement);
someElement.innerHTML = '<other-element></other-element>';
someElement.querySelector('other-element') instanceof OtherElement1; // This should evaluate to true.
someElement.remove();
someElement.innerHTML = '<other-element></other-element>';
someElement.querySelector('other-element') instanceof OtherElement1; // Should this evaluate to true?

@sorvell
Copy link
Author

sorvell commented Nov 21, 2024

I think the ideal behavior is that the registry lookup acts similar to ownerDocument: the registry used is always the last one that was relevant for the element by virtue of being created in or attached to a root with a registry.

Therefore I'd expect the last 2 instanceof's to be OtherElement3 (the element defined in shadowRoot2) and not OtherElement1 (the global version).

@rniwa
Copy link
Collaborator

rniwa commented Nov 21, 2024

Oh oops, I meant to say OtherElement3 for the second case. The last case being OtherElement3 is problematic in that it would mean we'd need to forever remember the original registry from which an element was created.

@rniwa
Copy link
Collaborator

rniwa commented Nov 21, 2024

Also, we have to deal with cases where elements from different scoped custom element registries coexist:

class OtherElement1 extends HTMLElement { };
customElements.define('other-element', OtherElement1);

const registry1 = new CustomElementRegistry;
class SomeElement extends HTMLElement { };
registry1.define('some-element', SomeElement);
class OtherElement2 extends HTMLElement { };
registry1.define('other-element', OtherElement2);

const registry2 = new CustomElementRegistry;
class OtherElement3 extends HTMLElement { };
registry2.define('other-element', OtherElement3);

const shadowRoot1 = createConnectedShadowTree(registry1);
const shadowRoot2 = createConnectedShadowTree(registry2);
const element1 = shadowRoot1.createElement('some-element');
const element2 = shadowRoot2.createElement('other-element');
element1.appendChild(element2);
element2.innerHTML = '<other-element></other-element>';
element2.querySelector('other-element'); // Is this OtherElement2 or OtherElement3?

@sorvell
Copy link
Author

sorvell commented Nov 21, 2024

The last case being OtherElement3 is problematic in that it would mean we'd need to forever remember the original registry from which an element was created.

Not the original registry, right? Just the last one it was attached to...

@sorvell
Copy link
Author

sorvell commented Nov 21, 2024

element2.querySelector('other-element'); // Is this OtherElement2 or OtherElement3?

Using the logic I'm proposing:

  1. element2 is customized in registry2 so it is OtherElement3.
  2. element2 is then attached to element1 in registry1 which means its "active registry" becomes registry1.
  3. Setting element2.innerHTML uses its "active registry," so element2.querySelector('other-element') is OtherElement2 from registry1.

@rniwa
Copy link
Collaborator

rniwa commented Nov 21, 2024

What happens if we removed element2 before setting innerHTML?

@sorvell
Copy link
Author

sorvell commented Nov 21, 2024

What happens if we removed element2 before setting innerHTML?

Using the rule in #1040 (comment), removing should not change the "active registry" so the result is unchanged.

@rniwa
Copy link
Collaborator

rniwa commented Nov 21, 2024

Ok, other examples:

class OtherElement1 extends HTMLElement { };
customElements.define('other-element', OtherElement1);

const registry1 = new CustomElementRegistry;
class SomeElement extends HTMLElement { };
registry1.define('some-element', SomeElement);
class OtherElement2 extends HTMLElement { };
registry1.define('other-element', OtherElement2);

const registry2 = new CustomElementRegistry;
class OtherElement3 extends HTMLElement { };
registry2.define('other-element', OtherElement3);

const shadowRoot1 = createConnectedShadowTree(registry1);
const shadowRoot2 = createConnectedShadowTree(registry2);
shadowRoot1.innerHTML = '<div></div>';
shadowRoot1.querySelector('div').innerHTML = '<some-element></some-element>';
const otherElement = shadowRoot2.createElement('other-element');
shadowRoot1.querySelector('some-element').appendChild(otherElement);
otherElement.innerHTML = '<other-element></other-element>';
const someElements = shadowRoot1.querySelectorAll('some-element');
someElements[0] instanceof OtherElement2; // Should this evaluate to true?
someElements[1] instanceof OtherElement2; // Should this evaluate to true?

@rniwa
Copy link
Collaborator

rniwa commented Nov 21, 2024

Okay, so I think I understand @sorvell's proposal. An element is associated with the registry of a shadow tree to which it last belonged. To put it another way, every time an element is inserted into a shadow tree, its associated registry changes to the registry of that shadow root.

@annevk and I discussed an alternative approach, which is to permanently associate each element with the registry from which it was created. Previously, we thought this will incur extra pointer per element and therefore not permissible in terms of memory cost. But we can mostly avoid this cost if the common scenarios involve all nodes in a given shadow tree sharing the same registry. In that scenario, we can continue to associate the shared registry with its ShadowRoot/Document. Whenever an element has a registry that is distinct from this shared registry, we need to set a bit flag on the node indicating that the element is in this state, and then we just need a side table mapping each such element to its registry.

@rniwa rniwa closed this as completed Nov 21, 2024
@rniwa rniwa reopened this Nov 21, 2024
@sorvell
Copy link
Author

sorvell commented Nov 21, 2024

@annevk and I discussed an alternative approach, which is to permanently associate each element with the registry from which it was created.

What happens in this case and would this be a breaking change?

customElements.define('x-foo', class XFoo extends HTMLElement {});
customElements.define('x-bar', class XBar extends HTMLElement {});
const template = document.createElement('template');
template.innerHTML = `<x-foo></x-foo>`;
const clone = template.content.cloneNode(true);
// it has the registry from the template content's document which is empty?
const xFoo = clone.firstChild; 
document.body.append(clone);

xFoo instanceof XFoo // is this true?

xFoo.innerHTML = `<x-bar></x-bar>`;
xBar = xFoo.firstChild;

xBar instanceof XBar // is this true?

@annevk
Copy link
Collaborator

annevk commented Nov 21, 2024

I think if the null registry case behaves the same as "I need to look at my parent for the registry" there would not be a breaking change.

@sorvell
Copy link
Author

sorvell commented Nov 21, 2024

In response to #1040 (comment), following the rule in the explainer:

someElements[0] instanceof OtherElement2; // Should this evaluate to true? 
// false, this is otherElement, which is OtherElement3
someElements[1] instanceof OtherElement2; // Should this evaluate to true?
// true, since it was created while attached to a root with registry1

@sorvell
Copy link
Author

sorvell commented Nov 21, 2024

@annevk

"I need to look at my parent for the registry"

Noting that this is a change from the proposal in the explainer which says the registry is found by getting the context node's root (aka getRootNode())

What happens here?

const registry1 = new CustomElementRegistry();
registry1.define('x-foo', class XFooScoped extends HTMLElement {});
const shadowRoot1 = createConnectedShadowTree(registry1);

const div = document.createElement('div');
shadowRoot1.append(div);
div.innerHTML = `<x-foo></x-foo>`;
xFoo = div.firstChild;

xFoo instanceof XFooScoped // is this true?
customElements.define('x-foo', class XFooGlobal extends HTMLElement {});
xFoo instanceof XFooGlobal // is this true?

@annevk
Copy link
Collaborator

annevk commented Nov 21, 2024

In that case the div would have the global registry as that's how you created it. So it would pull x-foo from there.

@sorvell
Copy link
Author

sorvell commented Nov 21, 2024

In that case the div would have the global registry as that's how you created it. So it would pull x-foo from there.

I think it's likely important to be able to understand what will happen when setting innerHTML on an element (at the least for testing). If this is the case, wouldn't we need a new API for "what registry what I created in"?

shadowRoot1.append(div1);
shadowRoot1.append(div2);
div1.innerHTML = `<x-foo></x-foo>`;
div2.innerHTML = `<x-foo></x-foo>`;

It seems fairly unintuitive to me that these x-foo elements might be totally different.

@annevk
Copy link
Collaborator

annevk commented Nov 21, 2024

I think all models have some amount of non-intuitiveness, but not changing the registry seems the most predictable. If we need an API for testing we could consider adding something to WebDriver or some such, for sure. It's usually best to wait a bit though and develop these things in baby steps. Doing it all at once usually results in oversights.

@sorvell
Copy link
Author

sorvell commented Nov 21, 2024

not changing the registry seems the most predictable

It seems most predictable to me to always use the registry of your root node, and this was the idea in the proposal.

As far as I can tell, there hasn't been an explicit argument made about why to change this other than implementation feasibility. If I understand correctly, the case that requires more information than getRootNode() could provide is when the root doesn't have an explicit registry (e.g. it's an unconnected element).

Perhaps we can focus on that case and consider independently how it should behave such that it is both feasible to implement and the behavior is acceptable.

It seems like we have these options for this case:

  1. use the registry of the root to which the element was last connected
  2. use the registry of the root in which the element was created
  3. use no registry
  4. use the global registry

Which of these would have implementation feasibility concerns? Only (1)?

From a behavior perspective, I think (1) makes most sense, but probably only (4) would be unacceptable.

@annevk
Copy link
Collaborator

annevk commented Nov 21, 2024

It wasn't really about implementation feasibility. I think it's rather weird behavior that when you create an element in registry1 and then append it to some subtree of registry2 further new child elements will now use registry2, despite the element being from registry1.

@sorvell
Copy link
Author

sorvell commented Nov 21, 2024

think it's rather weird behavior that when you create an element in registry1 and then append it to some subtree of registry2 further new child elements will now use registry2, despite the element being from registry1.

This makes sense to me because an element's children are not part of the shadowRoot it "owns" and where it can have an explicit registry. They are part the element's public API that it shouldn't really know much about or have much control over.

@annevk
Copy link
Collaborator

annevk commented Nov 25, 2024

I should have taken a bit more time to fully explain where my idea came from. I was thinking that apart from making the registry association with the element immutable, another benefit this brings is that it would allow for registries to be independent of shadow roots. We often had community feedback that features are too tied to shadow roots and this feature doesn't have to be. This would make it possible to reuse elements from various libraries in your document without adopting shadow roots at all.

@annevk annevk changed the title [scoped-registries] Finding a definition for disconnected elements Finding a definition for disconnected elements Dec 11, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants