Introduction to Creating Web Components

John Au-Yeung - Jan 30 '20 - - Dev Community

Subscribe to my email list now at http://jauyeung.net/subscribe/

Follow me on Twitter at https://twitter.com/AuMayeung

Many more articles at https://medium.com/@hohanga

Even more articles at http://thewebdev.info/

As web apps get more complex, we need some way to divide the code into manageable chunks. To do this, we can use Web Components to create reusable components that we can use in multiple places.

Web Components are also isolated from other pieces of code so it’s harder to accidentally modify them from other pieces of code and create conflicting code.

In this article, we’ll look at the different parts of a Web Component and how to create basic ones.

Parts of a Web Component

A Web Component has 3 main parts. Together they encapsulate the functionality that can be reused whenever we like without fear of code conflicts.

  • custom element — a set of JavaScript APIs that allow us to define custom elements and their behavior, which can be used as we desired in the UI
  • shadow DOM — set of JavaScript APIs for attaching the encapsulated shadow DOM tree of the element. It’s rendered separately from the main document DOM. this keeps the element’s features private, so they can be scripted without the fear of conflicting with other parts of the document
  • HTML templates — the template or slot elements enable us to write markup templates that aren’t displayed on the rendered page. They can be reused multiple times as the basis of the custom elements structure.

Creating Web Components

To create Web Components, we do the following steps:

  1. Create a class or function to specify the web component functionality.
  2. Register our new custom element using the CustomElementRegistry.define() method, passing the element name to be defined and the class or function which the functionality is specified, and optionally what element it inherits from
  3. Attach a shadow DOM to the custom element using the Element.attachSHadow() method. Add child elements, event listeners, etc. to the shadow DOM using normal DOM methods
  4. Define HTML templates using the template and slot tags. We use regular DOM methods to clone the template and attach it to our shadow DOM.
  5. Use custom element wherever we like on our page like any other regular HTML element

Basic Examples

The CustomElementRegistry keeps a list of custom elements that are defined. This object lets us register new custom elements on a page and return information about what custom elements are registered etc.

To register a new custom element on a page, we use the CustomElementRegistry.define() method. It takes the following arguments:

  • a string representing the name of the element. A dashed is required to be used in them. They can’t be single words.
  • a class object that defines the behavior of the element
  • an optional argument containing an extends property which specifies the built-in element our element inherits from if any

We can define a custom element as follows:

class WordCount extends HTMLElement {  
  constructor() {  
    super();  
    const shadow = this.attachShadow({  
      mode: 'open'  
    });  
    const span = document.createElement('span');  
    span.textContent = this.getAttribute('text').split(' ').length;  
    const style = document.createElement('style');  
    style.textContent = 'span { color: red }';  
    shadow.appendChild(style);  
    shadow.appendChild(span);  
  }  
}

customElements.define('word-count', WordCount);

In the code above, we attach a shadow DOM to our document by calling attachShadow . mode set to 'open' means that the shadow root is accessible from JavaScript outside the root. It can also be 'closed' which means the opposite.

Then we create a span element, where we set the text content to the text attribute’s length after we split the words in it wherever there’s a space.

Next, we created a style element and then set the content to color: red .

Finally, we attached them both to the shadow DOM root.

Notice that in our class, we extended the HTMLElement . We’ve to do this to define a custom element.

We can use our new element as follows:

<word-count text='Hello world.'></word-count>

There’re 2 types of custom elements. They are:

  • autonomous custom elements — which are standalone and don’t inherit from standard HTML elements. We can create them by referencing the name directly. For example, we can write <word-count>, or document.createElement("word-count").
  • customized built-in elements — these inherit from basic HTML elements. We can extend one of the built-in HTML elements to create these kinds of elements. We can use them by writing <p is="word-count">, or document.createElement("p", { is: "word-count" }).

The kind of custom element we created before are autonomous custom elements. We can create customized elements by writing:

class WordCount extends HTMLParagraphElement {  
  constructor() {  
    super();  
    const shadow = this.attachShadow({  
      mode: 'open'  
    });  
    const span = document.createElement('span');  
    span.textContent = this.getAttribute('text').split(' ').length;  
    const style = document.createElement('style');  
    style.textContent = 'span { color: red }';  
    shadow.appendChild(style);  
    shadow.appendChild(span);  
  }  
}

customElements.define('word-count', WordCount, {  
  extends: 'p'  
});

Then put in our page by writing:

<p is='word-count' text='Hello world.'></p>

As we can see, autonomous custom elements and customized built-in elements aren’t that different. The only differences are that we extend HTMLParagraphElement instead of HTMLElement .

Then we used the is attribute to reference the custom element instead of using the element name in the customized built-in element.

Internal vs. External Styles

We can also reference external styles as follows instead of using internal styles as we had above. For example, we can write:

const linkElem = document.createElement('link');  
linkElem.setAttribute('rel', 'stylesheet');  
linkElem.setAttribute('href', 'style.css');  
shadow.appendChild(linkElem);

This references styles from style.css . We created a link element just like we do in HTML and normal DOM manipulation.

Creating Web Components is simple. We just have to define a class or function for the functionality and then put it in the custom elements registry by using the customElements.define method.

We can extend existing elements like p elements or create ones from scratch. Also, we can add internal styles or reference external ones by creating a link element and referencing external files.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .