Web Component Starter Template

September 2023

This is my base line starter template for a web component. Note that it's my first attempt at this so there's probably thing that could be imporved in terms of effifiency, but it's all working as expected

JavaScript
The Component JavaScript
customElements.define('wc-starter', 
  class extends HTMLElement {    

    static get observedAttributes() {
      return ['data-externaltointernal']
    }

    constructor() {
      super()
      this.attachShadow({mode: 'open'})
      const tempHolder = document.createElement("div")
      const tempString = `
  <template id="starterComponent">
    <p>From attribute: <span id="initialAttr">-</span></p>
    <p class="wcHighlight">Highlighted from external style sheet set via attribute</p>
    <p>From slot on page: <slot name="slotContent"></slot></p>
    <label for="ctrl">Internal component ranger input:<label>
    <input type="range" id="ctrl" name="ctrl" />
    <p>From internal component range input: <span id="output"></span></p>
    <p>From external range input: <span id="externaltointernaloutput"></span></p>
  </template>`
      tempHolder.innerHTML = tempString
      const el = tempHolder.childNodes[1].content.cloneNode(true)
      const controller = el.getElementById("ctrl")
      const output = el.getElementById("output")
      controller.addEventListener("input", (event) => {
        output.innerHTML = event.target.value
        this.dataset.sendit = event.target.value
      })
       this.shadowRoot.append(el)
    }

    getStyleSheet() {
      if (this.dataset.stylesheet !== undefined) {
        const styles = document.createElement('link')
        styles.setAttribute('rel', 'stylesheet')
        styles.setAttribute('href', this.dataset.stylesheet)
        this.shadowRoot.append(styles)      
      }
    }

    loadAttribute() {
      const initialAttr = this.hasAttribute('initialAttr')
        ? this.getAttribute('initialAttr')
        : "No initialAttr Found"
      this.shadowRoot.getElementById("initialAttr").innerHTML = initialAttr 
    }

    attributeChangedCallback(name, oldValue, newValue) {
      if (name === "data-externaltointernal" && newValue !== oldValue) {
        this.shadowRoot.getElementById("externaltointernaloutput").innerHTML = newValue 
      }
    }

    connectedCallback() {
      this.getStyleSheet()
      this.loadAttribute()
    }

  }
)
Tango
External view of component range:
JavaScript
The Page JavaScript
const handleInsideMutations = (mutationList, observer) => {
    for (const mutation of mutationList) {
        if (mutation.attributeName === 'data-sendit') {
            const oldValue = mutation.oldValue
            const newValue = mutation.target.attributes.getNamedItem(
                mutation.attributeName
            ).value
            if (newValue !== oldValue) {
                fromComponent.innerHTML = newValue
            }
        }
    }
}

const handleExternalInput = (event) => {
    const newValue = event.target.value
    document.getElementById('wcExample').dataset.externaltointernal = newValue
}

const init = () => {
    document.getElementById('externalInput').addEventListener('input', handleExternalInput)
    const theComponent = document.getElementById('wcExample')
    const observer = new MutationObserver(handleInsideMutations)
    observer.observe(theComponent, { attributes: true })
}
document.addEventListener('DOMContentLoaded', init)

Features

TODO

    Single call (doesn't define class before adding it)

    Includes template directly inside JavaScript

    Call an external stylesheet

    Get content from a slot

    Get data from a attribute set in parent element call

    Get feedback inside the component from a form inside the component

    Get feedback outside the component from a form inside the component

    Get feedback inside the component from a form outside the component

end of line