Web Components

Álvaro José Agámez Licha

Software Developer

https://github.com/aagamezl

@aagamezl

Introduction

Web Components is a suite of different technologies allowing you to create reusable custom elements (with their functionality encapsulated away from the rest of your code) and utilize them in your web apps.

Web components are based on existing web standards. Features to support web components are currently being added to the HTML and DOM specs, letting web developers easily extend HTML with new elements with encapsulated styling and custom behavior.

What are Web Components, anyway?

Web components are based on four main specifications:

  • Custom Elements
  • Shadow DOM
  • ES Modules
  • HTML Template

Custom Elements

The Custom Elements specification lays the foundation for designing and using new types of DOM elements.

class MyComponent extends HTMLElement {
  connectedCallback() {
    this.innerHTML = `<h1>Hello world</h1>`;
  }
}
    
customElements.define('my-component', MyComponent);

<my-component></my-component>

Shadow DOM

The shadow DOM specification defines how to use encapsulated style and markup in web components.

let shadow = elementRef.attachShadow({mode: 'open'});
let shadow = elementRef.attachShadow({mode: 'closed'});

let myShadowDom = myCustomElem.shadowRoot;

Open means that you can access the shadow DOM using JavaScript written in the main page context. If you attach a shadow root to a custom element with mode: closed set, you won't be able to access the shadow DOM from the outside.

ES Modules

The ES Modules specification defines the inclusion and reuse of JS documents in a standards based, modular, performant way.

HTML Template

The HTML template element specification defines how to declare fragments of markup that go unused at page load, but can be instantiated later on at runtime.

<template id="book-template">
  <li>
    <span class="title"></span> &mdash;
    <span class="author"></span>
  </li>
</template>

<ul id="books"></ul>
const fragment = document.getElementById('book-template');
const books = [
  { title: 'The Great Gatsby', author: 'F. Scott Fitzgerald' },
  { title: 'A Farewell to Arms', author: 'Ernest Hemingway' },
  { title: 'Catch 22', author: 'Joseph Heller' }
];

books.forEach(book => {
  // Create an instance of the template content
  const instance = document.importNode(fragment.content, true);

  // Add relevant content to the template
  instance.querySelector('.title').innerHTML = book.title;
  instance.querySelector('.author').innerHTML = book.author;

  // Append the instance ot the DOM
  document.getElementById('books').appendChild(instance);
});


The basic approach for implementing a web component generally looks something like this:

 

  1. Create a class or a function in which you specify your web component functionality.
  2. Register your new custom element using the CustomElementRegistry.define() method, passing it the element name to be defined.
  3. If required, attach a shadow DOM to the custom element using Element.attachShadow() method.
  4. If required, define an HTML template using <template> and <slot>.
  5. Use your custom element wherever you like on your page, just like you would any regular HTML element.

Custom Element Reactions

A custom element can define special lifecycle hooks for running code during interesting times of its existence. These are called custom element reactions.

class AppDrawer extends HTMLElement {
  constructor() {
    // always call super() first in the constructor.
    super();
    ...
  }
  static get observedAttributes() { return ['c', 'l']; }

  connectedCallback() {
    ...
  }
  disconnectedCallback() {
    ...
  }
  attributeChangedCallback(attrName, oldVal, newVal) {
    ...
  }
}

constructor: An instance of the element is created or upgraded. Useful for initializing state, settings up event listeners, or creating shadow dom. See the spec for restrictions on what you can do in the constructor.

connectedCallback: Called every time the element is inserted into the DOM. Useful for running setup code, such as fetching resources or rendering. Generally, you should try to delay work until this time.

disconnectedCallback: Called every time the element is removed from the DOM. Useful for running clean up code.

attributeChangedCallback(attrName, oldVal, newVal): Called when an observed attribute has been added, removed, updated, or replaced. Also called for initial values when an element is created by the parser, or upgraded. Note: only attributes listed in the observedAttributes property will receive this callback.

adoptedCallback(): The custom element has been moved into a new document (e.g. someone called document.adoptNode(el)).

class AppDrawer extends HTMLElement {
  constructor() {
    // always call super() first in the constructor.
    super();
    ...
  }
  connectedCallback() {
    ...
  }
  disconnectedCallback() {
    ...
  }
  attributeChangedCallback(attrName, oldVal, newVal) {
    ...
  }
}

Web Components

By Alvaro Agamez

Web Components

  • 516