0

I created a header element which at its initial state shows loading

export const FmHeader = (data) => {
  fetch("../../templates/fm-header/index.html")
    .then((response) => response.text())
    .then((html) => HeaderElement(html));
};

const HeaderElement = (html, data) => {
  class FmHeader extends HTMLElement {
    constructor() {
      super();

      var el = this.attachShadow({ mode: "open" });
      el.innerHTML = html;
      if (data) {
        const artistName = el.querySelector(".artist_name");
        artistName.innerText = "data.artistName;";
        artistName.classList.remove("skeleton");
      }
    }
  }

  customElements.define("fm-header", FmHeader);
};

I'm calling it like so:

(function(){ 
    FmHeader();
    setTimeout(() => {
        FmHeader({data: 'some data from server'});
    }, 1000);
})()

I understand the error - that the same element can't be redefined. But is there a way to achieve something like this?

A basic code pen version

relidon
  • 2,142
  • 4
  • 21
  • 37
  • Use the [<>] button in the editor, and create an executable SO snippet, not many people will copy/paste your code parts and make it work – Danny '365CSI' Engelman May 22 '22 at 12:05
  • @Danny'365CSI'Engelman I can't get that to work, it throws `ReferenceError: HTMLElements is not defined` error – relidon May 22 '22 at 12:37
  • @Danny'365CSI'Engelman I create a code pen https://codepen.io/cezme4/pen/xxYLqLq?editors=1011 – relidon May 22 '22 at 12:52
  • Your code tries the ``define("fm-header", `` a second time (for every call to ``FmHeader``) You can't do that; Custom Elements can only be defined **once**, and can not be removed. – Danny '365CSI' Engelman May 22 '22 at 13:05

1 Answers1

0
  • Add content to Web Components with data-attributes, and <slot> (only in shadowDOM)

  • Attributes are only available in the connectedCallback, as the constructor can run when the Element is not in the DOM (eg. document.createElement("my-artist")

  • Multiple styling options, see: ::slotted CSS selector for nested children in shadowDOM slot

customElements.define("my-artist", class extends HTMLElement {
  constructor() {
    super()
     .attachShadow({mode:"open"})
     .innerHTML = `<style>`+
     `::slotted([slot="instruments"]) { background: gold }`+
     `</style>`+
     `<h1 part='artistname'></h1>`+
     `<div part='instruments'>Played: <slot name="instruments">No instruments specified</slot></div>`+
     `<slot>some facts here</slot>`+
     ``;
  }
  connectedCallback(){
    this
     .shadowRoot
     .querySelector("[part='artistname']")
     .innerText = this.getAttribute("name");
  }
});
*::part(artistname){
  /* style shadowDOM parts from global CSS */
  font-size:1.2em;
  margin:0;
  background:blue;
  color:gold;
}
body{
  font:12px Arial; /* inheritable styles DO style shadowDOM*/
}
<my-artist name="John">John was my favorite!
  <span slot="instruments">Many instruments</span>
</my-artist>
<my-artist name="Paul"></my-artist>
<my-artist name="George">
   <span slot="instruments">drums</span>
</my-artist>
<my-artist name="Ringo"><span slot="instruments">drums</span></my-artist>

Notes:

  • note how the slot order is maintained (see John)

  • George does not show the default some facts here, because the <my-artist> innerHTML contains spaces (and linebreaks); which get slotted to the default/unnamed <slot>

Danny '365CSI' Engelman
  • 16,526
  • 2
  • 32
  • 49