Building custom HTML elements with web components
Tags: webcomponents, javascript
Published: 2020-11-29 18:38:11

---
title: Building custom HTML elements with web components
published: true
description: Imagine creating reusable, encapsulated HTML elements, without any framework
tags: webcomponents, javascript
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/hqakscwpi9asoirdr55b.png
---

###Imagine building reusable, encapsulated HTML elements, without any framework! With web components, we can certainly create it!

## What are Web components?
Web components are a set of web platform APIs that allows to create custom, reusable and encapsulated HTML elements that can be used in any document or web app. Web components consist of 3 different technologies.

1. **Custom elements**, a set of Javascript APIs that permits to create custom HTML elements and define their behavior.
2. **Shadow DOM**, is used to encapsulate javascript and styling to specific components.
3. **HTML templates**, is used to declare markup that goes unused at page load, but it can be instantiated later at runtime.

## Custom elements
Custom elements are the foundation of web components. With the `CustomElemets` API, we are creating new HTML elements based on desired behavior and logic. 

Constructing a custom element is similar to construct a component in Vue, React or insert-flavor-of-the-week framework, but without the need for a framework. These custom elements can be used in any document, both directly in HTML or in web applications. Another advantage of using web components, since these custom elements are supported by browsers, is that they don't become obsolete (in contrast to SPA framework).

In order to build a custom element, a javascript class is necessary to extend `HTMLElement` and define the class with a tag name. A fundamental version of a custom element:

{% codepen https://codepen.io/marcusalfredsson/pen/WNxqEGB %}


Despite the fact that this example isn't advanced, it allows being used as a starting block. The example illustrates that the javascript class extends `HTMLElement` allowing the component to be registered by the browser. It's important to define the custom HTML element with a tag in order to use it from the document. Defining a tag is achieved with `customElements.define('hello-world', HelloWorld)`. After fulfilling these steps, we have built a custom element, ready to be used in any document by simply writing `<hello-world></hello-world>`.

Additionally, extending `HTMLElement` allows access to the API used by HTML elements, for example, lifecycle events. In the exposed case, lifecycle event `connectedCallback` is executed when the component is inserted into the DOM. `connectedCallback` is the right location for adding initial content to elements or fetching data to be rendered in a component.

**NOTE**: A custom element always has a dash in the tag name, for example `my-component`, `hello-world` or `whats-up`. Browser vendors have bound not to use dashes in HTML tags, to avoid conflicts in tag name.

## HTML template
With HTML templates, we define HTML that will be instantiated later at runtime.


```html
<template>
  <p>Hello world</p>
</template>
```


The code snippet above renders an empty page as a result of the template's content aren't displayed in the browser. With this powerful technique, we can define and store HTML in the DOM and display the content when desired. To display the content of a template we need to use javascript.


```javascript
  const template = document.querySelector('template');
  const node = document.importNode(template.content, true);
  document.body.appendChild(node);
```


Start with fetching the template from the DOM, next copy the template to the `node` variable using `importNode` and at the end insert the newly created `node` into the DOM. 

The `importNode` copies the template's content and in consequence it can be reused in several places for a document. After being executed, the DOM is similar to:


```html
<template>
  <p>Hello world</p>
</template>
<p>Hello world</p>
```


A great aspect about templates is the possibility to include any HTML, style or scripts. As a result templates are a good location for styling our component:


```html
<template id="counter">
  <style>
    button {
      background-color: red;
      color: white;
      padding: 4px;
    }
  </style>
  <button>Click me</button>
  <span id="times">0</span>
</template>
```


## Shadow DOM
The DOM (Document Object Model) represents the structure of the HTML document. The DOM has a tree structure that models a document with a parent-children relationship.

The DOM API has absent support for encapsulation. This characteristic makes it difficult to create reusable, encapsulated custom elements.

Encapsulation is however possible in the shadow DOM and is accessible to use javascript and styling to custom elements. When creating a shadow DOM, a subtree is attached to one DOM element. The newly created shadow DOM subtree is encapsulated from the rest of the document and our shadow DOM subtree cannot affect the residual part of the document.

Aside from encapsulation, the API for the DOM and the shadow DOM work similarly. Furthermore functions like `querySelector`, `textContent`, `getElementById` etc. from the API can still be used.


{% codepen https://codepen.io/marcusalfredsson/pen/YzWobNg %}


This exemplifies how shadow DOM is attached to the root of a custom element with `this.attachShadow({mode: 'open'})`. Now the generated shadow DOM will encapsulate javascript, HTML and styling inside the component. 


```html
<template id="counter">
  <style>
    button {
      background-color: red;
      color: white;
      padding: 4px;
    }
  </style>
  <button>Click me</button>
  <span id="times">0</span>
</template>
<my-counter>
  #shadow-root
    <style>
      button {
        background-color: red;
        color: white;
        padding: 4px;
      }
    </style>
    <button>Click me</button>
    <span id="times">0</span>
</my-counter>
```

After we render our component the final result of the DOM is outlined as in the present example.

## Wrapping up
Without using any SPA framework, we are able to create encapsulated, reusable web components that are a great benefit to worldwide users. 

Considering that web development becomes increasingly complex, it's reasonable to invest more development in the web platform. From this perspective, I believe web components are a great complement to SPA frameworks such as Vue and React. They don't substitute each other, but it's very suitable to build these custom HTML elements without any framework.

Thanks for reading!