Re: [w3c/webcomponents] Should custom elements be adoptable if so, how? (#512)

OK, @dominiccooney pointed out a problem with "stay custom with same definition" semantics, and now I am not sure we are on the right path here.

### The problem

A common pattern is for people to use a `<template>` to instantiate their element's contents. However, this pattern fails with our current setup for custom element registries and our proposed setup for adopting. Consider:

```html
<!DOCTYPE html>
<template id="xfoo">
  <x-bar>hello</x-bar>
</template>

<script>
"use strict";
customElements.define("x-bar", class XBar extends HTMLElement { ... });

const template = document.querySelector("template#xfoo");

class XFoo extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: "open" });
    this.shadowRoot.appendChild(template.content.cloneNode(true));
  }
}

customElements.define("x-foo", XFoo);
</script>
```

The problem is, template contents belong to a special browsing context-less Document. Thus, any elements created inside the template contents are never custom (step 2 of [look up a custom element definition](https://html.spec.whatwg.org/multipage/scripting.html#look-up-a-custom-element-definition) returns null). Cloning the template contents doesn't change that. And then, per the proposed adoption semantics in this thread, when we append the resulting document fragment, the cloned non-custom elements will carry their non-customness with them into XFoo's shadowRoot---not at all what we intended.

I hope we can all agree this is a problem? This kind of template + custom elements + shadow DOM scenario has been part of the web components story for as long as I have been involved, and it seems like we accidentally broke it. (Or at least, discovered that it wasn't on very solid footing.)

### Proposed solutions

@dominiccooney and I came up with several solutions, trying to take into account the various opinions expressed in this thread about defaults, added concepts, and more.

- "Auto downgrade": this is essentially the old "downgrade, then upgrade" from https://github.com/w3c/webcomponents/issues/512#issuecomment-226281431. This has the drawback of running a constructor twice. However, `adoptedCallback()` has the potential of allowing some kind of teardown/reset of state, which could help...
- "Manual downgrade": we keep the "stay custom with same definition" semantics by default, but elements which wish to work with `<template>` can use some new API, say `customElements.downgrade(element)`, inside their `adoptedCallback()`.
- "Branching adoption": when adopting elements, if the element already has a custom element definition, keep it. However, if it does not have one, then look up the potential custom element definition in the destination document, and if such a definition exists, assign it. This prevents running any constructors twice, and solves the template problem (as well as the similar-but-less-common `responseXML` and `DOMParser` problems).
   - There's a variant of this where we branch based on whether the source document had a browsing context, instead of based on whether the being-adopted element had a custom element definition. The difference being that "has a browsing context" would only catch the template/responseXML/DOMParser cases, whereas "has a custom element definition" would also allow moving elements between "normal" documents and the element "lighting up" if only the destination document has a definition.

Of these I am warming up to "branching adoption"... The "has a browsing context" version even seems somewhat principled. I guess we conceptualize that as "elements get a chance to become custom the first time their node document has a browsing context", which is either at creation, or at adoption.

I'm really sorry to retread this after we already went through the discussion and seemingly settled on a good solution. But I think we missed something big, and fixing it will be worth it!

---
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/w3c/webcomponents/issues/512#issuecomment-232410812

Received on Wednesday, 13 July 2016 16:31:34 UTC