Web Components Using Vanilla JS

Melike Kavcıoğlu
5 min readJan 22, 2022

--

If you are a developer for some time, you know the value of reusing elements that you have been created before. For example, every time you are defining properties of a circle with given radius and color, you may choose to write something like that

circle1.radius = 10;
circle1.color = 'red';
circle2.radius = 3;
circle2.color = 'blue';

But for the sake of clean code, creating a function would be more conventional.

circle1.update(10, ‘red'); 
circle2.update(3, ‘blue);

We can explain Web Components as the solution for the reusability of UI. You might be familiar with frameworks such as React, Angular etc. which allows you to create reusable components. When I first started to developing Front-end, I totally ignored the capabilities of Vanilla JavaScript and learned React and Angular like it was the only way to manage this. However, pure, old, good JavaScript supports this feature without requiring any other framework or library.

In this story, we will see how to create Web Components and attach them to the HTML using only JavaScript.

Starting With The Files

Firstly, I have to files index.html and index.js in my repository. We can start with standard HTML5 content, and attach our element later.

index.html — Initial File

HTMLElement and Shadow DOM

In index.js file, we will define a class called “MyCircle” which will extend the “HTMLElement” class so that we will be able to represent our class in HTML document tree.

In constructor, we should create our Shadow DOM. Shadow DOM allows us tp attach a hidden, separate DOM to the element. We can add styles so that it cannot leak outside. This means that I can have two different element with the same class name however they will not effect each other if the styling is added to the shadow. This helps us to avoid conflicts among components. {mode: ‘open’} states that Shadow DOM is accessible using JS in our component.

index.js — Shadow

We will add our render function to create the HTML view. With shadow, we are able to add innerHTML and define it using `` quotes. InnerHTML might not be the best solution here, but since we are avoiding using any libraries such as Lit, innerHTML is satisfactory for us.

index.js — Render function

Life Cycle Callbacks

Now, it is a good time to mention “life cycle callbacks” in the web components. There are four types of life cycle callbacks in HTMLElement web component. This methods are automatically called when custom element’s behavior is affected. There are four types of callback functions defined.

  1. connectedCallback: When the custom element first attached to the document’s DOM, this function is invoked.
  2. attributeChangedCallback: We will see how to define attributes to our custom element, later. This method is triggered when the values of the attributes are updated.
  3. adoptedCallback: This will be called if the custom element is moved to a different document.
  4. disconnectedCallback: Invoked when the custom element is removed or disconnected from the document’s DOM.

Back to our project, we want to include connectedCallback because when our custom element is connected to the document, we would like to render it. We need to add our render() function in connectedCallback.

index.html — connectedCallback()

Defining the Custom Element as an HTML Tag

We need to access our custom element from the html file. Like <button>, we need a way to attach a tag. customElements.define() is a function to do that. First parameter will be the tag and the second element will be our custom element class that we are rendering.

I also added the initial styling to the innerHTML. When we have the attributes, we will be changing those values.

index.js — Defining HTML Tag

Now we will be table to use<my-circle> tag in our index.html. We will also add our index.js as script to add the custom element.

index.html — Using HTML Tag
Result

Adding More To The Custom Element

Now, i will two inputs to my custom element to trigger an event. The first input element will take a positive number to pass radius value and the second input will determine the color of my circle. For now, we don’t care about styling. Our render function is now:

index.js — Render function

Setting Attributes to the HTML Tag

HTML Elements may have attributes. For example, <input> tag has a attribute called “type” so if we give it a value of “number” it does not allow us to enter any text in the input element.

For <my-circle>, we will have two attributes “radius” and “color”.

To make these attributes valid, we need to define them as properties in our MyCircle class.

Firstly, we need add tell our class that which attributes this tag can have. It’s done with a static get methods called observedAttribute. After that we will attach attributeChangedCallback life cycle hook to invoke when the attributes our changed (it will be done when the inputs are changed). We want this methods to re-render our shadow html view. To use this hook, we also need to define get and set methods for our custom attributes. See how I used these properties in style of the MyCircle.

Handling Events

Now, all I need to do is that connecting color and radius attributes with corresponding input values and attach a custom event to them.

Firstly, I need to pass the initial values to the inputs. Then I will create the functions to listen when the input values are changed. We need to add these functions to connectedCallback to attach when the DOM is created and to the attributeChangedCallback to re-attach the event listeners when the attributes are changed.

index.js — Final

And this is the our final index. “setAttribute” methods invoke attributeChangedCallback method so that we render the component to see the result. Let’s see the final product:

This is it!

Buy me a coffee for your support ❤

--

--

Melike Kavcıoğlu
Melike Kavcıoğlu

Written by Melike Kavcıoğlu

Full Stack Developer & UX Designer

Responses (3)