Custom Element Demo (label with input and time)

by Alnis Smidchens

HTML

<x-app></x-app>

JavaScript

class XAppElement extends HTMLElement {
  // run once in the whole application context
  static {
    customElements.define('x-app', XAppElement)
    const styles = new CSSStyleSheet; styles.replaceSync(`
x-app {
  >label {
    font-family: system-ui;
    >input {
      margin-top: 8px;
      padding: 8px;
      border-radius: 8px;
      border: 1px solid #000;
      font-size: 1.2em;
    }
  }
}
`)
    document.adoptedStyleSheets.push(styles)
  }

  // getters for element references
  get #$input() { return this.querySelector('input') }
  get #$output() { return this.querySelector('output') }
  get #$time() { return this.querySelector('time') }

  // allocated once when element is constructed
  #created = new Date

  // called every time element enters DOM
  connectedCallback() {
    if (!this.innerHTML) this.#init()
  }

  // manually called once when element enters DOM
  #init() {
    console.log('Initializing')
    this.innerHTML = `
<label>
  Your name is: <output></output> (<time></time>)<br>
  <input type="text">
</label>
`
    // initialize content that will never change
    this.#$time.innerText = this.#created.toISOString()

    // explicitly declare event => render relationship
    this.#$input.addEventListener('input', () => this.#render())

    // do first render
    this.#render()
  }

  // manually called when content may have changed
  #render() {
    const text = this.#$input.value
    console.log('Text: ' + text)
    this.#$output.innerText = text || '(Start typing...)'
  }
}