[scoped-registries] Finding a definition for disconnected elements
sorvell opened this issue · comments
Assuming the behavior described in the explainer's Finding a custom element definition section, consider the following.
- Define
x-foo
in the global and a scoped registry. - In a shadowRoot using that registry, create an element:
el = shadowRoot.createElement('div')
. - Now create and
x-foo
viainnerHTML
: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.
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.
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.
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);
}