Introduction to Web Components

Ishant Oberoi
Level Up Coding
Published in
5 min readMay 7, 2020

--

Photo by Blaz Erzetic on Unsplash

What are Web Components?

Code reuse is the mantra of development. We strive to create a code snippet that encapsulates its functionality, serves as a solution to a single problem or use case. In web development, we might want to create a custom element that has its own style, own script and own markup. This custom element should be reusable across the web. i.e. websites and web apps.
Web Components help us deliver this and more! They are a set of web platform APIs that help us create custom HTML tags with associated style and script. They can work across browsers and can be used with any JavaScript library or framework that works with HTML.

Building blocks

Web Components are defined using three major building blocks:

  1. Custom Elements: A set of JavaScript APIs that help us design custom DOM elements with the associated behaviour.
  2. Shadow DOM: A set of JavaScript APIs for attaching an encapsulated “shadow” DOM tree to an element. This markup is rendered separately from the main document DOM. This helps define elements features as private, so there is no chance of the elements style or script having a collision with the script and style on the main document.
  3. HTML Template: helps us write markup that is not displayed in the rendered page. This markup can be used to define the structure of the custom element. It is defined using <template> and <slot> elements.

Custom Elements

Custom elements are of two types:

  1. Autonomous Custom Elements: They are standalone elements i.e. they do not inherit from standard HTML elements. They inherit from the generic HTMLElement class. For example: <word-count> or document.createElement(‘word-count’).
  2. Customised built-in elements: They inherit from basic HTML elements. They inherit from specific element class say HTMLUListElement or HTMLImageElement. You use them by specifying the name of the custom element as the value of the is attribute. For example: <ul is=”styled-list”> or document.createElement(‘ul’, {is: “styled-list” }).

Lifecycle Callbacks

There are several lifecycle callbacks available that fire in different points in the custom elements lifecycle. We can make use of these while defining the custom element. Some of the available callbacks are:

  1. connectedCallback: called each time the custom element is appended into a document-connected element. This will happen every time the node is moved, and may happen before the element’s contents have been fully parsed.
  2. disconnectedCallback: called each time the custom element is disconnected from the document’s DOM
  3. adoptedCallback: called each time the custom element is moved to a newdocument.
  4. attributeChangedCallback: called each time one of the custom element’s attributes is added, removed, or changed. Which attributes to notice change for is specified in a static get observedAttributes method.

Shadow DOM

Shadow DOM allows hidden DOM trees to be attached to elements in the regular DOM tree. The shadow DOM tree starts with a shadow root, underneath which can be attached to any elements you want, in the same way as the normal DOM.

Terminology

  1. Shadow host: The regular DOM node that the shadow DOM is attached to.
  2. Shadow tree: The DOM tree inside the shadow DOM.
  3. Shadow boundary: the place where the shadow DOM ends, and the regular DOM begins.
  4. Shadow root: The root node of the shadow tree.

You attach the shadow root to any element using attachShadow method.

const shadow = elementReference.attachShadow({mode: 'open'});
/* OR */
const shadow = elementReference.attachShadow({mode: 'closed'});

Here, if the mode is set to open we can access the shadow DOM using JavaScript written in the main page, else not.

HTML Template

HTML Template helps us define the structure of a custom element.

Example

Let us create an Autonomous Custom Element <code-snippet>. This element will take in a language name, function description and a code snippet to show it with some predefined styling. You may use it in a documentation page for your new project!
Will use <slot> elements here. <slot> helps us define placeholders inside the <template>. We can pass any markup later when the element is used in the document to replace the <slot> with the actual markup. We need to give the slot attribute/property of our markup the name of the slot we want to replace inside the template.

<template id="code-snippet">
<style>
div {
border: 1px dotted #867f7f;
background-color: #efefef;
padding: 5px;
text-align: center;
margin-top: 10px;
}
p {
margin: 0;
padding: 0;
}
strong {
font-weight: bold;
color: blue;
}
</style>
<div>
<p>
<strong><slot name="language">language</slot></strong>
</p>
<p>
<em><slot name="description">function description</slot></em>
</p>
<p>
<code><slot name="code">code snippet</slot></code>
</p>
</div>
</template>

refer this template inside a custom element definition.

/* code-snippet.js */class CodeSnippet extends HTMLElement {
constructor() {
super();
var template = document.getElementById('code-snippet');
const shadowRoot = this.attachShadow({ mode: 'open' });
var templateContent = template.content;
shadowRoot.appendChild(templateContent.cloneNode(true));
}
}
/* register your element with the CustomElementRegistry using the define method. Pass the name to be used as tag (here code-snippet) and the class name(here CodeSnippet)*/customElements.define('code-snippet', CodeSnippet);

Now you can consume the custom element in HTML document.

Note: The script that registers the custom element has to be loaded after the DOM is parsed. Hence either use defer to load the script or add the script tag at the bottom of the <body> tag inside the html document.

/* index.html */
<head>
<script src="code-snippet.js" defer></script>
</head>
<body>
<h2>Web Component - Code Snippet</h2>
<code-snippet>
<span slot="language">JavaScript</span>
<span slot="description">Date function</span>
<span slot="code">new Date()</span>
</code-snippet>
<code-snippet>
<span slot="language">PHP</span>
<span slot="description">Date function</span>
<span slot="code">date("Y/m/d")</span>
</code-snippet>
<template id="code-snippet">
...
</template>
</body>

Thats it! We have created a very simple web component.

Benefits

Since Web Components are built on web standards they come in with some great benefits.

  1. Interoperability: they can work with multiple projects all with different technology stacks. As they do not rely on any framework or library they can work with variety of tech stacks.
  2. Longevity: since they are based on web standards there will be minimum or no rewrite to fit them into newer technologies. Hence longer lifespan.
  3. Productivity: iterating over the already written components helps us save time and effort. You can develop more features on top of them much faster.
  4. Brand Consistency: curating components as per your need and having them use across the teams regardless of the tech stack helps you maintain a consistent look and feel across the application.
  5. Code Sharing: you can share the same logic and consistent look between the teams and in different projects.

In nutshell, Web Components — WORA — Write Once Run Anywhere.

--

--