@vonage/element-f

A functional shim to custom element definition.

Usage no npm install needed!

<script type="module">
  import vonageElementF from 'https://cdn.skypack.dev/@vonage/element-f';
</script>

README

element-f

A functional shim to custom element definition.

Installation

npm i @vonage/element-f

Basics

In order to define a custom-element, you only need one definition function:

import elementF from "@voange/element-f";

const MyElement = elementF(()=> {
  // Your logic goes here  
  const shadow = this.attachShadow({mode: 'open'});
});

To tap into lifecycle events, this function can use the "life" event emitter:

const MyElement = elementF((life)=> {
    const shadow = this.attachShadow({mode: 'open'});
    // Listen once to when this component connects to a document 
    life.once('connect', ()=> shadow.innerHTML = `I'm Alive!`);
});

The "life" event emitter supports three methods:

  • once(name, fn)
    on(name, fn)
    - Registers fn for events of name name. once() will invoke fn once.
    • name - The name of the event to listen to
    • fn(payload) - The function to be called when an event occurs
      • payload - An object containing information regarding the event
  • off(name, fn) - Removes an event handler previously registered using on or once.

The following events are thrown:

  • connect - Fired upon connectedCallback. Delivers no payload.
  • disconnect - Fired upon disconnectedCallback. Delivers no payload.
  • attribute - Fired when an observed attribute changes. Delivers name, previousValue and newValue as payload.

To observe attributes, just add their list to elementF call:

const MyElement = elementF((life)=> {
    life.on('attribute', ({ name, previousValue, newValue })=> {
        // name can be "one" or "two"
    });
}, ["one", "two"]);

Usage Examples

To define a custom element using standard class notation, you'd write something like:

class MyButton extends HTMLElement {
    
    constructor(){
      super();
      console.log(`I'm alive!`);
    }

    static get observedAttributes(){
        return ['disabled'];
    }
    
    attributeChangedCallback(name, oldValue, newValue) {
      this.classList.toggle('disabled', newValue); 
    }

    connectCallback() {
      this.innerHTML = "<b>I'm an x-foo-with-markup!</b>";
    }
}

To defining the same element using element-f would look like this:

const MyButton = elementF((life)=> {
  
  life.on('connect', ()=> { 
    this.innerHTML = "<b>I'm an x-foo-with-markup!</b>"; 
  });
  
  life.on('attribute', ({ name, newValue, oldValue })=> {
    this.classList.toggle('disabled', newValue); 
  });
  
  console.log(`I'm alive!`);

}, ['disabled']);

What does Element-F solve?

Element-F supplies a stylistic framework, not a fundamental solution to a problem. If you're happy with OOP-styled constructs, you would probably not draw much enjoyment from using it :)