DEV Community

James Robb
James Robb

Posted on • Edited on

Custom Elements

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);
Enter fullscreen mode Exit fullscreen mode

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) {}
...
Enter fullscreen mode Exit fullscreen mode

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 gettable (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'];
}
...
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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);

Enter fullscreen mode Exit fullscreen mode

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:

Current support for Web Components APIs from the caniuse documentation at the time of writing this article

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.

Resources

Top comments (15)

Collapse
 
jamesrweb profile image
James Robb

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.

Collapse
 
smalluban profile image
Dominik Lubański • Edited

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).

Thread Thread
 
jamesrweb profile image
James Robb

Looks interesting, I will give the repo docs a proper read tomorrow while I am travelling - Thanks for the heads up.

Collapse
 
darryldexter profile image
Darryl D.

I'm interested in hearing about some frameworks. I wonder if I can use something like preact opposed to polymer.

Thread Thread
 
jamesrweb profile image
James Robb

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.

Thread Thread
 
darryldexter profile image
Darryl D.

I looked into stencil, I was hoping for something a little more lightweight.

Collapse
 
rico345100 profile image
.modernator

Awesome! Can I translate this post as Korean? I will reveal the source of course!

Collapse
 
jamesrweb profile image
James Robb

Yes, totally ok with me, thanks for asking first, link me the article when it’s up, that’s all I ask! :)

Collapse
 
rico345100 profile image
.modernator

Thanks, I just finished translation! Here's the link:
modernator.me/article/javascript/i...

Thread Thread
 
jamesrweb profile image
James Robb

Cool, well done! looks good according to a korean friend of mine and thanks for the shoutout too!

Thread Thread
 
rico345100 profile image
.modernator

No problem! Thank you for great post!

Thread Thread
 
rico345100 profile image
.modernator

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!!

Thread Thread
 
jamesrweb profile image
James Robb

Thanks for the update!

All good, my next web components article will be out in 5 weeks. Stay tuned!

Collapse
 
labtifo profile image
Labreche Abdellatif

check this custom elemnet tha i've made github.com/labTifo/super-coolor-pi... it's a super cool color picker

Collapse
 
jamesrweb profile image
James Robb

Awesome 👏! Nice component