Prerequisites
- For the purpose of this guide I will be building this component using VSCode, you can install this editor using the following link: https://code.visualstudio.com/download
- Once downloaded open the program and select the following: Code > Preferences > Extensions
- In the search field (Search Extensions in Marketplace), enter “Live Server“ and select the extension by the Author “Ritwick Dey“.
- Install this plugin onto your VSCode
- Next download the attached starter files below:
[Add download link here!]
Overview
This guide is going to walk you through creating your first Web Component, a simple Employee Card containing avatar, name and job title. Follow the steps below to get started.
Section 1 - Custom Elements
In order to create a custom element we need to use ES6 Class Syntax (Classes - JavaScript | MDN) to extend upon the HTMLElement API (HTMLElement - Web APIs | MDN).
Classes in simple terms are a template for creating objects in javascript. They encapsulate data within code so we are able to work on that data.
1. In your employeeCard.js file add the following code:
class EmployeeCard extends HTMLElement {
}
You have now created a class for your new web component (Employee Card). This will be the object that end up using to define your custom element.
We next need to utilise two Javascript methods:
constructor(): a special method of a class for creating and initializing an object instance of that class. In our example this is the EmployeeCard class.
super(): used to call the constructor() of its parent class to access the parent's properties and methods. In our example this gives us access to the properties and methods inside of HTMLElements.
2. Inside of your newly created “class“ lets add the following methods:
class EmployeeCard extends HTMLElement {
constructor() {
super();
}
}
By calling the super() method in the constructor() method, we call the parent's constructor method and gets access to the parent's properties and methods.
You can find out more about constructor and super here:
constructor - JavaScript | MDN
For the purpose of testing lets now provide this class some dummy data so we can test our custom element once we have created it.
3. Inside of your constructor method, lets add the following code:
class EmployeeCard extends HTMLElement {
constructor() {
super();
this.innerHTML = `<p>I am dummy data</p>`
}
}
In our above example, the this keyword is used as a reference to our EmployeeCard object therefore enforcing its innerHTML to be: <p>I am dummy data</p> when it comes to using our custom element.
You can read more on the this keyword here:
The last step within our javascript file is to define our new custom element. Within our Window we have access to the customElements property (Window: customElements property - Web APIs | MDN) inside of this property we have access to the “define“ method (CustomElementRegistry: define() method - Web APIs | MDN).To define a new custom element the define method requires two parameter define(name, constructor).
4. Let's define our new custom element. Add the following code beneath the class you previously created:
class EmployeeCard extends HTMLElement {
constructor() {
super();
this.innerHTML = `<p>I am dummy data</p>`
}
}
window.customElements.define("employee-card", EmployeeCard);
And Voila, you have a now created your first custom element. Now how do we use it?… Well its nice and simple, check out the next step.
5. Inside of your index.html lets add the following tag inside of your div:
<employee-card></employee-card>
Now lets see it working, whilst viewing your index.html file in VSCode right click the code and select the following option: Open With Live Server
A new tab should be displayed in your browser of choice, If all is working with your code you should now see the following:
[ADD EXAMPLE IMAGE HERE]
If you are seeing the above PERFECT!inspect your code [cmd, option, i] and you will be able to see your new custom element within the page
[ADD EXAMPLE IMAGE HERE]
Well Done!
Section 2 - Shadow DOM (Not As Scary As It Sounds!)
To start this section I want you to think of the DOM you already know and use as the “Light DOM“ and the new DOM we are going to learn about in this section as the “Shadow DOM“.
Attaching a Shadow DOM to any element is simple with the attachShadow() method (Element: attachShadow() method - Web APIs | MDN). This method takes in an option parameter allowing you to specify a mode for the shadowRoot. These options are as followed:
- mode: "open" - Elements of the shadow root are accessible from JavaScript outside the root
- mode: "closed" - Denies access to the node(s) of a closed shadow root from JavaScript outside it
For the purpose of this guide we are going to be leaving the mode as “open“.
6. Let's attach our Shadow DOM to our EmployeeCard.
class EmployeeCard extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
this.innerHTML = `<p>I am dummy data</p>`
}
}
window.customElements.define("employee-card", EmployeeCard);
If you check this again on the front end of the site you will notice two things:
- Firstly “I am dummy data“ is no longer appearing
- Secondly we now see something unfamiliar to us in our code #shadow-root (open) (ShadowRoot - Web APIs | MDN)
Lets get our dummy text appearing again… Now we this shadow root in place by attaching our Shadow DOM we need to update our code a little reference this new root node.
7. Update your inner HTML with the following code:
class EmployeeCard extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
this.shadowRoot.innerHTML = `<p>I am dummy data</p>`
}
}
window.customElements.define("employee-card", EmployeeCard);
If you can now see your dummy data again PERFECT! you have now learnt how to attach a Shadow DOM to your custom element and access its Shadow Root. Now lets add the final piece to what makes up a web component, HTML Templates.
Section 3 - HTML Templates
We have now learnt how to create a custom element and attach the Shadow DOM to it. Now lets add the final piece to this web component puzzle, HTML templates (Using templates and slots - Web APIs | MDN).
Template elements allow us to create a flexible template that can be used to populate the content of a Shadow DOM.
8. Above the EmployeeCard class we have created lets create a template tag using javascript:
const template = document.createElement("template");
class EmployeeCard extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
this.shadowRoot.innerHTML = `<p>I am dummy data</p>`;
}
}
window.customElements.define("employee-card", EmployeeCard);
9. Now he have access to a template tag lets populate its inner HTML with some basic HTML Markup and styling that we wish to use for every employee card we create:
const template = document.createElement("template");
template.innerHTML = `
<article>
<img src="https://avatars.dicebear.com/api/bottts/stefan.svg" width="100" height="100" />
<div class="content">
<h1>Name Here</h1>
<p>Job Title Here</p>
</div>
</article>
<style>
article {
display: grid;
grid-template-columns: 100px 1fr;
align-items: center;
gap: 1rem;
padding: 0.25rem 1rem;
margin-bottom: 1rem;
background: lightblue;
border: 4px solid #f2f2f2;
color: #222;
font-family: 'open-sans', Arial;
}
h1,
p {
margin: 0;
padding: 0;
}
img {
margin: 0.3rem 0;
border-radius: 50%;
box-shadow: 0 0 0 2px #fff;
}
p {
font-style: italic;
}
</style>
`;
class EmployeeCard extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
this.shadowRoot.innerHTML = `<p>I am dummy data</p>`;
}
}
window.customElements.define("employee-card", EmployeeCard);
We have now created the HTML template for our employee card, but how do we attach this template to our Shadow DOM? lets see:
10. Lets clone our template to the Shadow DOM using the cloneNode method (Node: cloneNode() method - Web APIs | MDN) and delete our “I am dummy data line of code“:
const template = document.createElement("template");
template.innerHTML = `[SEE TEMPLATE CODE ABOVE, REMOVED TO KEEP BLOCK SHORT]`;
class EmployeeCard extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
const shadow = this.shadowRoot;
shadow.appendChild(template.content.cloneNode(true));
}
}
window.customElements.define("employee-card", EmployeeCard);
Clone node in basic terms simply duplicated the node in which the method was called. If you visit the front end of the site you should see your first web component working! pretty cool right? Although currently its not very dynamic, let's do something about that.
11. Clear out the test data from our HTML template:
const template = document.createElement("template");
template.innerHTML = `[SEE TEMPLATE CODE ABOVE, REMOVED TO KEEP BLOCK SHORT]`;
class EmployeeCard extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
this.shadowRoot.appendChild(template.content.cloneNode(true));
}
}
window.customElements.define("employee-card", EmployeeCard);
So where are we going to get our dynamic content from you may be wondering. For the purpose of this guide we are going to be using data attributes attached to our custom element.
12. Lets add the following code to our custom element tag inside of our index.html:
<employee-card
name="ADD YOUR NAME"
title="ADD YOUR JOB TITLE"
src="https://avatars.dicebear.com/api/bottts/stefan.svg"
></employee-card>
Now we need to pass these attributes into our template, we can do this rather easily with simple Javascript and our new found knowledge of accessing the shadow root.
13. Lets pass our data attributes into our HTML template using javascript, beneath where we attached cloned our HTML template insert the following code:
const template = document.createElement("template");
template.innerHTML = `[SEE TEMPLATE CODE ABOVE, REMOVED TO KEEP BLOCK SHORT]`;
class EmployeeCard extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
this.shadowRoot.appendChild(template.content.cloneNode(true));
this.shadowRoot.querySelector("img").src = this.getAttribute("src");
this.shadowRoot.querySelector("h1").innerHTML = this.getAttribute("name");
this.shadowRoot.querySelector("p").innerHTML = this.getAttribute("title");
}
}
window.customElements.define("employee-card", EmployeeCard);
Congratulation you have now created your first dynamic web component! Have a play around now with your code and see what else you could add to make it better. You can even duplicate your <employee-card></ employee-card> to put multiple on your page give it a go.