Search code examples
javascripthtmlweb-component

Is there a way to add custom attributes to custom elements


While messing around with custom elements I wondered if one could use custom attributes within the elements (and possibly within their children too). I know VueJS does something similar with attributes like v-bind, v-for, etc; and I know there's probably a lot more going on under the hood there than I realize. I've tried registering custom elements and attempting to retrieve them like so:

<new-element cool="Awesome!"> </new-element>

class NewElement extends HTMLElement {
    constructor() {
        super();
        this.coolAttr = this.getAttribute("cool");
    }
}

customElements.define("new-element", NewElement);

However, when loading the page (in Google Chrome for me) the "custom" attributes disappear, and any attempt at getting them retrieves null. Is there a way to "register" these custom attributes, or do I have to stick with data- attributes?


Solution

    • Attributes become available in the connectedCallback,
      they are not available yet in the constructor
      Unless the Custom Element is PARSED (in the DOM) BEFORE the Element is defined!!

    • Also be aware the attributeChangedCallback runs before the connectedCallback
      for Observed attributes

    • Also see: https://andyogo.github.io/custom-element-reactions-diagram/

    .as-console-row-code {
        font: 12px Arial!important;
        background:yellow;
        color:darkred;
    }
    .as-console-row:after{ display:none!important }
    <before-element cool="Awesome?">FOO</before-element>
    
    <script>
      class NewElement extends HTMLElement {
        log( ...args ){
          console.log(this.nodeName, `cool:${this.getAttribute("cool")}`,"\t\t\t",...args );
        }
        static get observedAttributes() {
          return ["cool"];
        }
        constructor() {
          const name = "constructor"; // CAN! run code BEFORE super()!
          // super() sets AND returns the 'this' scope
          super().log(name);
        }
        connectedCallback() {
          this.log("connectedCallback", this.innerHTML || "innerHTML not parsed yet");
          // be aware this.innerHTML is only available for PARSED elements
          // use setTimeout(()=>{...},0) if you do need this.innerHTML
        }
        attributeChangedCallback(name, oldValue, newValue) {
          this.log(`attributeChangedCallback name:${name}, old:${oldValue}, new:${newValue}`);
        }
      }
      customElements.define("before-element", class extends NewElement {});
      customElements.define("after-element",  class extends NewElement {});
    </script>
    
    <after-element cool="Awesome!!">BAR</after-element>