A partial archive of discourse.wicg.io as of Saturday February 24, 2024.

Custom Elements in Templates

Tessalator
2019-09-25

I’m putting a custom element in a template and then “selecting” it out to Window level. It seem to loose its identity as a custom element.

// In page
<template id="holder">
    <my-element field="A"></my-element>
</template>

<script type="module">
    class MyElement extends HTMLElement {
        constructor() {
            super();
        }
        get Field() { return this.getAttribute("field");}
    }

    window.customElements.define("my-element", MyElement);
</script>

<script>
    const frag = document.querySelector("#holder").content;
    const el = frag.querySelector("my-element");
    console.log({el});
</script>

When <my-element> is a page level and not in a template I can read xxx.Field. When it comes from within a template it seems to no longer act like the custom element - xxx.Field isn’t defined. The script and selecting it and custom elements are both at “Window” level, so shouldn’t it “cast” correctly without having to be attached to document?

guest271314
2019-09-25

You can define a variable at window that is a Promise from whenDefined() with value set to the custom element name, set the second script type to "module" to handle the asynchronous code execution, within second <script type="module"> you can use then() to get the custom element name, get the class with customElements.get(<custom element name>), setPrototypeOf() el to the constructed custom element object, import the node into the current document

<template id="holder">
    <my-element field="A"></my-element>
</template>

<script type="module">
    const customElementName = "my-element";
    class MyElement extends HTMLElement {
        constructor() {
            super();
        }
        get Field() { return this.getAttribute("field");}
    }
    window.customElements.define(customElementName, MyElement);
    window.customElementPromise = window.customElements.whenDefined(customElementName)
                                  .then(_ => customElementName);
</script>

<script type="module">
  if (window.customElementPromise && window.customElementPromise instanceof Promise) {
    window.customElementPromise
    .then(customElementName => {
      const frag = document.querySelector("#holder").content;
      const el = frag.querySelector("my-element"); 
      const CustomElement  = window.customElements.get(customElementName);
      Object.setPrototypeOf(el, new CustomElement);
      document.importNode(el, true);
      console.log(el.Field);
    })
    .catch(console.error);
  } else {
    console.assert("customElementName" in window, [window["customElementName"]])
  }
</script>