So, in a library that I'm creating that uses custom elements, you obviously need to define the class in the CustomElementsRegistry
before you may instantiate it.
As of right now, this is being solved with a decorator:
class Component extends HTMLElement {
static register (componentName) {
return component => {
window.customElements.define(componentName, component);
return component;
}
}
}
@Component.register('my-element')
class MyElement extends Component { }
document.body.appendChild(new MyElement());
This works, however, I would like to automatically register the custom element upon instantiation of the class (so that the author does not have to add the decorator to every single component that they write). This could maybe be accomplished via a Proxy
.
My problem, though, is that when I attempt to use a Proxy on the constructor, and attempt to return an instance of the target, I still get Illegal Constructor
, as if the element has never been defined in the registry.
This obviously has to do with the way I am instantiating the class inside of the proxy, but I'm unsure of how to do it otherwise. My code is as follows:
Please run in latest Chrome:
class Component extends HTMLElement {
static get componentName () {
return this.name.replace(/[A-Z]/g, char => `-${ char.toLowerCase() }`).substring(1);
}
}
const ProxiedComponent = new Proxy(Component, {
construct (target, args, extender) {
const { componentName } = extender;
if (!window.customElements.get(componentName)) {
window.customElements.define(componentName, extender);
}
return new target(); // culprit
}
});
class MyElement extends ProxiedComponent { }
document.body.appendChild(new MyElement());
How can I continue the chain of inheritance inside of the proxy without losing the context of the fact that I'm instantiating the MyElement
class so that it doesn't throw the Illegal Constructor
exception?
Extending HTMLElement ensures the custom element inherits the entire DOM API and means any properties/methods that you add to the class become part of the element's DOM interface. Essentially, use the class to create a public JavaScript API for your tag.
One of the key features of the Web Components standard is the ability to create custom elements that encapsulate your functionality on an HTML page, rather than having to make do with a long, nested batch of elements that together provide a custom page feature.
Autonomous custom elements are new HTML tags, defined entirely by the author. They have none of the semantics of existing HTML elements, so all behaviors need to be defined by the author. Customized built-ins extend existing HTML elements with custom functionality. They inherit semantics from the elements they extend.
Custom Elements allow web developers to define new types of HTML elements. The spec is one of several new API primitives landing under the Web Components umbrella, but it's quite possibly the most important. Web Components don't exist without the features unlocked by custom elements: Define new HTML/DOM elements.
There were 2 problems:
new target()
created LibElement
instance, that is not registered as custom element. And here you got Illegal Constructor
error.LibElement
resulting DOM element will be <lib-element>
, cause you call new target
and at this point javascript have no idea about child class.The only way i found is to use Reflect
API to create right instance of object.
class LibElement extends HTMLElement {
static get componentName () {
return this.name.replace(/[A-Z]/g, char => `-${ char.toLowerCase() }`).substring(1);
}
}
const LibElementProxy = new Proxy(LibElement, {
construct (base, args, extended) {
if (!customElements.get(extended.componentName)) {
customElements.define(extended.componentName, extended);
}
return Reflect.construct(base, args, extended);
}
});
class MyCustomComponent extends LibElementProxy {}
class MyCustomComponentExtended extends MyCustomComponent {}
document.body.appendChild(new MyCustomComponent());
document.body.appendChild(new MyCustomComponentExtended());
And I really liked this idea of proxied constructor for auto-registration of custom elements )
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With