Differences between ways to dynamically instantiate a web component
Asked Answered
H

1

7

There are several ways a web component (autonomous custom elements only with regard to this question) can "come to life".

Are there notable differences between the three options below?

Option 1:

const foo = document.createElement('foo-element');
document.body.appendChild(foo);

Option 2:

const div = document.createElement('div');
div.innerHTML = '<foo-element></foo-element>'
const foo = div.firstElementChild;
document.body.appendChild(foo);

Option 3:

const foo = new FooElement;
document.body.appendChild(foo);

I wrote some Unit Tests based on a Karma/Mocha stack, and created my instances with option 3.

Is this sufficient, that means, can I rely on the components having identical state/behaviour using either method, or is it necessary to repeat all my tests with all different options of instantiating applied?

One of my web components fails to be instantiated using document.createElement because of an error:

VM977:1 Uncaught DOMException: Failed to construct 'CustomElement':
The result must not have attributes
at <anonymous>:1:10

The fact that the same component can be instantiated with no problems using new tells me that, behind the curtain, there must be notable differences especially between new FooElement and document.createElement('foo-element').

I can write three general tests to test all three ways of instantiating, for sure, but is this enough?

Or should all of my existing tests be run with all 3 options of instantiating?

Or asked differently:

Is every instance exactly the same after instantiating? (assuming there is no error)

Hayott answered 25/8, 2020 at 12:19 Comment(0)
U
4

The difference in the 3 methods is manifested if you register foo-element as a custom HTML element with the CustomElementRegistry.define() method. Based on my experimentation, the second method cannot take advantage of any special processing provided by registering the custom element. Also, the first method must be done as follows:

document.createElement("p", { is: "foo-element" });

where I have defined foo-element to extend the <p> tag as an example.

Anyway, an example can explain this better. In the code below I have defined FooElement to extend the <p> tag to automatically be initialized with the text "I am foo."

// Create a class for the element
class FooElement extends HTMLParagraphElement {
  constructor() {
    // Always call super first in constructor
    super();
    this.innerText = 'I am foo';
  }
}


// Define the new element (The CustomElementRegistry is available through the Window.customElements property):

customElements.define('foo-element', FooElement, { extends: 'p' });

Now execute the following snippet:

window.onload = function() {
    class FooElement extends HTMLParagraphElement {
      constructor() {
        // Always call super first in constructor
        super();
        this.innerText = 'I am foo';
      }
    }

    customElements.define('foo-element', FooElement, { extends: 'p' });

    const div1 = document.createElement('div');
    document.body.appendChild(div1);
    const foo1 = document.createElement("p", { is: "foo-element" });
    div1.appendChild(foo1);

    const div2 = document.createElement('div');
    document.body.appendChild(div2);
    div2.innerHTML = '<foo-element></foo-element>';

    const div3 = document.createElement('div');
    document.body.appendChild(div3);
    const foo3 = new FooElement();
    div3.appendChild(foo3);

};
<body>
</body>

We have created all three elements but only the thirst and third options have any effect as far as achieving the desired special processing. If you were to inspect the text you would see that the actual enclosing elements are in fact <p> tags.

As far as your DOMException is concerned, the first two methods that you showed should not cause an exception whether you have registered your element or not. However, the third method will throw an exception if FooElement is not a legitimate node such as is created by extending HTMLParagraphElement as in the above example. So, I would need more information on the exact circumstances of your exception.

Update

Here class FooElement does not inherit from a standard element and an exception is thrown:

window.onload = function() {
    class FooElement {
      constructor() {
      }
    }

    const div3 = document.createElement('div');
    document.body.appendChild(div3);
    const foo3 = new FooElement();
    div3.appendChild(foo3);

};
<body>
</body>
Unashamed answered 31/8, 2020 at 12:7 Comment(5)
In your example, obviously div2.innerHTML = '<foo-element></foo-element>'; makes no sense because your code extended HTMLParagraphElement. If anything, it would have to be div2.innerHTML = '<p is="foo-element"></p>';. Also I don't see how your example of <p is="foo-element"> is of any help here. The project only uses autonomous custom elements. Your answer honestly is not an answer; you touch the topic, that's it.Thank you nonetheless for trying.Jadotville
Your question was "Are there notable differences between the three options below?" I gave a long answer but the "punch line" was in the very first line of my answer: "The difference in the 3 methods is manifested if you register foo-element as a custom HTML element with the CustomElementRegistry.define() method." I went on to demonstrate what that difference was. Granted, yours were autonomous elements, so you could have felt free to ignore the exposition. But there are other people perhaps reading this. Sorry this was of no use to you. (more...)Unashamed
However, I did mention that if you do not inherit from an actual element, then option 3 should fail (it does for me). See new snippet.Unashamed
The new snippet obviously cannot work because FooElement does not extend HTMLElement. Also, it was never registered as a custom element.Jadotville
Only for the record (and for future readers), I did not pick this answer and it really doesn't deserve the bounty or the upvotes. Stackoverflow decided to give the bounty to this reply. It does not answer the question at all. It's unfortunate that by the looks of it, people have upvoted that probably never coded a single custom element.Jadotville

© 2022 - 2024 — McMap. All rights reserved.