Introduction
Custom Elements, a subset of Web Components, are one of the coolest things to hit the web in my opinion. Effectively, they allow us to build out components that are native to the web platform, instead of using intermediary libraries or frameworks like React, Angular, Vue, or otherwise.
In this post, I hope to show you what a Custom Element is, how one is built and how to put it on the page.
The foundations of a web component
All Custom Elements will share some common methods, an example of these can be seen in the code example below:
class MyComponent extends HTMLElement {
static get observedAttributes() {
return [];
}
constructor(...args) {
super(...args);
}
connectedCallback() {}
disconnectedCallback() {}
adoptedCallback() {}
attributeChangedCallback(attrName, oldVal, newVal) {}
}
window.customElements.define('my-component', MyComponent);
Let's break down what is actually happening here.
constructor()
A constructor must always be ideally declared and any parameters passed to the parent also.
The constructor is always ideally where any event listeners, etc would normally be implemented, for example:
...
constructor(...args) {
super(...args);
this.addEventListener('click', this.handleClick);
}
handleClick(event) {}
...
connectedCallback()
Called every time the element is inserted into the DOM.
Everytime the component is added anywhere in the page, at any time, it will fire this function.
disconnectedCallback()
Called every time the element is removed from the DOM.
If for example we delete the node or a parent node in the DOM tree, this function will fire since inherently it will remove the element from the aforementioned tree.
The disconnectedCallback()
will also run when the element is adopted elsewhere on the document or in a new page.
adoptedCallback()
Invoked each time the custom element is moved to a new document.
If the custom element is moved to a new page or document, this callback will fire.
attributeChangedCallback(attrName, oldVal, newVal)
The behaviour occurs when an attribute of the element is added, removed, updated, or replaced.
This function is fired whenever an attribute on the component is changed HOWEVER, only if the attribute that changed is currently being observed, which brings us to the observedAttributes()
.
observedAttributes()
Attributes of the custom element we actually want to observe changes upon
As you can see, this method is declared as static get observedAttributes()
and this clearly differs from other method declarations, this is because we want it inherited by any sub classes/components and we want to declare it only once to reference, heed, it is static
(set for all inheritors and itself) and get
table (for reference).
This function should return an array of strings where each string is the name of the attribute you wish to observe, for example:
...
static get observedAttributes() {
return ['id', 'my-custom-attribute', 'data-something', 'disabled'];
}
...
There are some other functions in the Custom Elements specification but these are the ones we would primarily use day to day.
A basic component
Let's build a basic component that says hello to a user.
The html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Custom Elements 101</title>
</head>
<body>
<hello-component data-name="James"></hello-component>
<script src="./hello-component.js"></script>
</body>
</html>
The javascript
class HelloComponent extends HTMLElement {
static get observedAttributes() {
return ['data-name'];
}
// custom methods
render() {
this.innerHTML = `Hello ${this.name}`;
}
get name() {
return this.getAttribute('data-name');
}
// lifecycle hooks
connectedCallback() {
this.render();
}
attributeChangedCallback(attrName, oldVal, newVal) {
this.render();
}
}
// add into the 'real' dom as a valid tag
window.customElements.define('hello-component', HelloComponent);
Load the index.html and you should see "Hello James" displayed on the page.
Now, open the inspector (DevTools) and change the data-name
attribute to something other than James. You will see we have inbuilt reactivity! Pretty sweet, right?
Granted, this is only a very basic, non-best-practice, 0 use case, default tutorial implementation but it does give you a rough introduction which we can build upon in future articles.
Browser support
Here is the current support for Web Components and all the APIs that facilitate them including Shadow DOM, Custom Elements (what we just looked into), HTML templates and slots and HTML imports:
Conclusions
Custom Elements allow us a framework free way of implementing reactive UI's when required. They do provide us alot of challenges, many of which we will look at in the future, but go forth and try them out, look into the other Web Component APIs also as when put together, they really allow us to make cool, strong, reactive elements which can do alot with very little.
Top comments (15)
In a future article I will probably have an article that goes into Polymer and similar frameworks but I thought it was too much to go into libraries/frameworks for a base level introduction article and thus skipped it here.
Give a try to hybrids. It's a new library for creating web components using unique functional API, which makes building custom elements super simple :)
If you have any questions, feel free to ask (I am an author of the library).
Looks interesting, I will give the repo docs a proper read tomorrow while I am travelling - Thanks for the heads up.
I'm interested in hearing about some frameworks. I wonder if I can use something like preact opposed to polymer.
As far as I know, preact and react both currently have problems with web components. Not 100% but pretty sure that's the case, would need to test again, last time I did, there was issues though. Polymer and Stencil are your best frameworks to test out, stencil is interesting to me as it is more cutting edge and still works but polymer is probably more stable and does the job a little better on the support front in my opinion.
I looked into stencil, I was hoping for something a little more lightweight.
Awesome! Can I translate this post as Korean? I will reveal the source of course!
Yes, totally ok with me, thanks for asking first, link me the article when it’s up, that’s all I ask! :)
Thanks, I just finished translation! Here's the link:
modernator.me/article/javascript/i...
Cool, well done! looks good according to a korean friend of mine and thanks for the shoutout too!
No problem! Thank you for great post!
Well, I just updated URL for translate this post here:
modernator.me/article/webcomponent...
Previous URL will results something like "not found" message box shown up from now.
I will research and post more about Web Components, so I separated to different category!
Anyway again, thanks for great article, definitely I'll be more search about this!!
Thanks for the update!
All good, my next web components article will be out in 5 weeks. Stay tuned!
check this custom elemnet tha i've made github.com/labTifo/super-coolor-pi... it's a super cool color picker
Awesome 👏! Nice component