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
orslot
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:
- Create a class or function to specify the web component functionality.
- 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 - 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 - Define HTML templates using the
template
andslot
tags. We use regular DOM methods to clone the template and attach it to our shadow DOM. - 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>
, ordocument.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">
, ordocument.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.