Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Modifying a Custom Element class after it's been defined

Is it possible to modify a custom element / web component class after it's been registered? This isn't something I'll typically (or ever) want to do in production code, but when prototyping or implementing a code-patching tool for development, it'd be useful. Here's what I've tried, unsuccessfully, so far:

document.body.appendChild(document.createElement('foo-bar')) // empty element

class FooBar extends HTMLElement {
  connectedCallback () {
    this.textContent = 'foo bar'
  }
}

customElements.define('foo-bar', FooBar) // element shows "foo bar"

FooBar.protoype.connectedCallback = function () {
  this.textContent = 'foo bar 2'
}

document.body.appendChild(document.createElement('foo-bar')) // element shows "foo bar", not "foo bar 2"

customElements.get('foo-bar').protoype.connectedCallback = function () {
  this.textContent = 'foo bar 2'
}

document.body.appendChild(document.createElement('foo-bar')) // element shows "foo bar", not "foo bar 2"

class FooBar2 extends HTMLElement {
  connectedCallback () {
    this.textContent = 'foo bar 2'
  }
}

customElements.define('foo-bar', FooBar2) // Exception thrown, 'foo-bar' already defined

In summation, modifying the class originally passed to the CustomElementsRegistry has no effect. Assigning to the prototype of what's in the registry has no effect. Attempting to assign a new class to the element in the registry isn't possible as an exception is thrown. All three of these behaviors are counter to my experience with almost every aspect of every other JavaScript API. It's extremely unusual, for better or worse, to not be able to mutate something unless it's set to be non-configurable. However, when I inspect if the objects are configurable, the runtime says they are!

Does anyone know of a way to modify/augment a custom element definition after it's been defined?

like image 573
james_womack Avatar asked Dec 14 '17 03:12

james_womack


People also ask

Can custom elements be self closing?

Custom elements cannot be self-closing because HTML only allows a few elements to be self-closing. Always write a closing tag ( <app-drawer></app-drawer> ).

How do I check if a custom element is defined?

The customElements. get() method can be used to check whether a given custom element has already been registered in the page. This can be used to find whether a given custom element name is available in the page or not, and prevent clashes with another custom element.

What is a custom element and when do we use them?

“What's The Point Of Custom Elements?” The basic idea is that if you create an element that always performs the same role and has the same set of properties and functions applied to it, then you should be able to name it after what it does.


1 Answers

There is no way to change a tag once defined, seems like it pulls in the class when defined.

I have found a way to dynamically reload the code behind a custom element by placing a wrapper class as the constructor, I now have hot reloading on custom elements, no more refreshing apps - the wrapper will need to handle attribute bubbling etc

basically you need to rely on the following pieces

  1. knowing what elements are not defined, ideally called whenever you performing changes to the DOM, alternatively you could place a setInterval for development. document.querySelectorAll(':not(:defined)')

  2. define the custom element inline so you can use the same constructor for ALL custom elements. customElements.define(elementName, class extends HTMLElement {...


document.querySelectorAll(':not(:defined)').forEach((ele, k) => {
        let elementName = ele.tagName.toLowerCase();
        if (!customElements.get(elementName)) {

            //defining custom element
            customElements.define(elementName, class extends HTMLElement {
                constructor() {
                    super();
                }

                disconnectedCallback() {
                    if (this._elementClass) {
                        this._elementClass.disconnectedCallback()
                    }
                }

                connectedCallback() {

                    let element = (this.tagName).toLowerCase();

                    //reload the class variable here eg: importScript/getScripts 
                    //dependent on whatever variable you want eg: cache id or 
                    //then make calls to the customElement class
                    this._elementClass = new NodeTest(this);
                    this._elementClass.connectedCallback(); 


...


var NodeTest = class {

    constructor(ele) {
        this.ele = ele;
    }

    disconnectedCallback() {

    }

    connectedCallback() {

        this.render();
    }

    render() {
        this.ele.innerHTML = '<h1>hheee</h1>';
    }
}
like image 150
Onyx Avatar answered Nov 02 '22 14:11

Onyx