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.
- Custom elements, a set of Javascript APIs that permits to create custom HTML elements and define their behavior.
- Shadow DOM, is used to encapsulate javascript and styling to specific components.
- 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:
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.
<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.
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:
<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:
<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.
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.
<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!
Top comments (1)