Search code examples
javascripthtmlclasscustom-element

How do I create a custom element using a JS class to create the properties for the element


So I know how to create a custom element and have 2 already made that I'm happy with. My two elems look something like this:

let menuPicker = Object.create(HTMLElement.prototype);

menuPicker.initialized = false;
menuPicker._init = function(){
...
}
...
menuPicker.attachedCallback = menuPicker._init;
menuPicker.createdCallback = menuPicker._init;
...
document.registerElement('menu-picker', {
    prototype: menuPicker
});

Which works and all, but this time around I want a JS class. Specifically this feature:

class test {
    get item(){...}
    set item(){...}
}

So I figured I could just do the following to easily implement this.

class listBox extends HTMLElement.prototype {
    initialized = false;
    constructor(props){
        super(props);
        this.attachedCallback = this._init;
        this.createdCallback = this._init;
    }
    _init () {
        if(this.initialized) return;
        this.initialized = true;
        this.style.display = "block";
        this.appendChild(document.createElement('div'));
    }
}
document.registerElement('list-box', {
    prototype: listBox
});

However I get the error Class extends value #<HTMLElement> is not a constructor or null. So now I'm stuck and can't find a method of using a class to construct a custom HTML element.

To simplify:

How do I create a custom element using a JS class to create the properties for the element?


Solution

  • Here is an example to create a custom element

      //ListBox.js
      export default class ListBox extends HTMLElement {
      // observe attributes of custom element
      static get observedAttributes() {
        return ["disp", "text"];
      }
    
      get myAction() {
        return this.hasAttribute("open");
      }
    
      set myAction(val) {
        if (val) {
          this.setAttribute("open", "");
        } else {
          this.removeAttribute("open");
        }
      }
    
      constructor() {
        super();
        this.initialized = false;
        this.div = document.createElement("div");
        // get and assigns value from props "disp"
        this.div.style.display = this.getAttribute("disp");
        // get and assigns value from props "text"
        this.div.innerHTML = this.getAttribute("text");
        this.appendChild(this.div);
      }
      connectedCallback() {
        // didMount
        // your methode here
        this.initialized = true;
        console.log("custom element added to page");
      }
    
      disconnectedCallback() {
        // unmount
        console.log("custom element remove from page");
      }
    
      attributeChangedCallback(name, oldValue, newValue) {
        // observed props
        console.log("props", arguments);
        // get and assigns value from props "text"
        if (name === "text" && oldValue !== newValue) {
          this.div.innerHTML = newValue;
        }
      }
    }
    

    in your html in the script tag which calls your index.js add type="module"

    <!DOCTYPE html>
    <html>
      <head>
      </head>
    
      <body>
        <div id="app">
          <list-box text="Some text here" disp="block"></list-box>
        </div>
        <script src="src/index.js" type="module"></script>
      </body>
    </html>
    

    In your index.js import your component and do Something

    import ListBox from "./component/ListBox";
    
    customElements.define("list-box", ListBox);
    
    // change props "text value"
    document
      .querySelector("list-box")
      .setAttribute("text", `Change the value of props "text"`);