How To Create Custom HTML Elements – Simple Examples

Welcome to a tutorial on how to create custom HTML elements. Once upon a time in the dark ages of the Internet, we are pretty much “stuck” to whatever HTML elements that the developers have provided us with. But things have changed ever since the introduction of Web Components.

The overly simplified and general steps to create a custom HTML element are:

  1. Define a class that extends HTML element – class MYELEMENT extends HTMLElement { /* BUILD YOUR ELEMENT */ }
  2. Register the class as a custom HTML element – customElements.define("my-element", MYELEMENT)

Yes, it is now possible to create our own custom HTML elements with the help of Web Components. But just how do we do that? Let us walk through a simple example in this guide – Read on!

ⓘ I have included a zip file with all the example source code at the start of this tutorial, so you don’t have to copy-paste everything… Or if you just want to dive straight in.

 

 

TABLE OF CONTENTS

Download & Notes Custom Element HTML Template
Useful Bits & Links The End

 

DOWNLOAD & NOTES

Firstly, here is the download link to the example code as promised.

 

EXAMPLE CODE DOWNLOAD

Click here to download the source code, I have released it under the MIT license, so feel free to build on top of it or use it in your own project.

 

QUICK NOTES

If you spot a bug, please feel free to comment below. I try to answer questions too, but it is one person versus the entire world… If you need answers urgently, please check out my list of websites to get help with programming.

 

 

CUSTOM HTML ELEMENT WITH JAVASCRIPT

All right, let us now get started with creating our own custom HTML element with Javascript.

 

THE HTML

1a-notice.html
<!-- (A) LOAD CUSTOM HTML ELEMENT -->
<script src="1b-notice.js"></script>
 
<!-- (B) CUSTOM HTML ELEMENT -->
<my-notice icon="info" text="Hello World!"></my-notice>
<my-notice icon="warn" text="A Warning!"></my-notice>

Spotted the “suspicious element”? Yes, my-notice is the custom HTML notification bar that will be created in Javascript.

 

THE DEMO

Go ahead – Do a right-click, inspect element on the above notification bars.

 

 

THE JAVASCRIPT

1b-notice.js
class Notice extends HTMLElement {
  // (A) CONSTRUCTOR
  constructor () {
    // (A1) NECESSARY TO CALL HTMLELEMENT CONSTRUCTOR
    super();

    // (A2) SHADOW DOM ELEMENTS
    this._wrap = document.createElement("div");
    this._icon = document.createElement("i");
    this._text = document.createElement("span");
    this._wrap.appendChild(this._icon);
    this._wrap.appendChild(this._text);

    // (A3) CSS STYLES
    // NOTE: SHADOW DOM CANNOT BE STYLED FROM EXTERNAL CSS
    // EITHER INLINE THE STYLES HERE, OR USE @IMPORT
    this._styles = document.createElement("style");
    this._styles.textContent = `
    div {
      padding: 10px;
      margin: 5px;
      font-size: 20px;
      background: #eee;
      border: 1px solid #ddd;
    }
    i {
      margin-right: 10px;
      font-style: normal;
    }
    div.info {
      background: #defaff;
      border: 1px solid #9ac8d0;
    }
    div.warn {
      background: #ffe4de;
      border: 1px solid #cc2500;
    }`;

    // (A4) ATTACH STYLES & ELEMENTS INTO SHADOW DOM
    this._shadow = this.attachShadow({mode: "open"});
    this._shadow.appendChild(this._styles);
    this._shadow.appendChild(this._wrap);
  }

  // (B) LISTEN FOR ATTRIBUTES CHANGE
  static get observedAttributes () { return ["icon", "text"]; }
  attributeChangedCallback (name, oldVal, newVal) {
    // (B1) SET ICON
    if (name=="icon") {
      this._wrap.className = newVal;
      switch (newVal) {
        default: this._icon.innerHTML = "X"; break;
        case "info": this._icon.innerHTML = "ⓘ"; break;
        case "warn": this._icon.innerHTML = "⚠"; break;
      }
    }

    // (B2) SET TEXT
    if (name=="text") { this._text.innerHTML = newVal; }
  }

  // (C) OPTIONAL CALLBACKS
  // (C1) WHEN  CUSTOM ELEMENT IS INSERTED INTO DOM
  connectedCallback() { console.log("Connected"); }

  // (C2) WHEN  CUSTOM ELEMENT IS REMOVED FROM DOM
  disconnectedCallback() { console.log("Disconnected"); }

  // (C3) WHEN  CUSTOM ELEMENT IS ADOPTED
  adoptedCallback() { console.log("Adopted"); }
}

// (D) REGISTER CUSTOM HTML ELEMENT
// NOTE: CUSTOM ELEMENT NAMES MUST CONTAIN DASH
// I.E. "MYNOTICE" OR "MY_NOTICE" WILL THROW AN ERROR
customElements.define("my-notice", Notice);

This looks extremely complicated, but keep calm and look closely. We are practically building our custom HTML element using class Notice extends HTMLElement.

  1. The constructor is where we use the “native” HTML elements to build the shadow DOM of our custom element.
  2. Set the attribute observers. That is, we update the icon and text whenever these attributes are changed.
  3. Optional listeners. If you need to do something when the custom element is inserted or removed from the DOM.
  4. Finally, register class Notice as <my-notice> HTML element. Take extra note of the naming convention here – All custom HTML elements must contain a dash.

For you guys who new to the shadow DOM, think of it as a “mini HTML document” that is contained within the custom element itself.

 

 

CUSTOM ELEMENT USING HTML TEMPLATE

Don’t like the above way of using pure Javascript to create a custom element? Here’s an alternative using an HTML template.

 

HTML TEMPLATE

2a-template.html
<!-- (A) NOTICE BOX TEMPLATE -->
<template id="noticetemp">
  <style>
  div {
    padding: 10px;
    margin: 5px;
    font-size: 20px;
    background: #eee;
    border: 1px solid #ddd;
  }
  i {
    margin-right: 10px;
    font-style: normal;
  }
  div.info {
    background: #defaff;
    border: 1px solid #9ac8d0;
  }
  div.warn {
    background: #ffe4de;
    border: 1px solid #cc2500;
  }
  </style>
  <div>
    <i></i>
    <span></span>
  </div>
</template>

<!-- (B) JAVASCRIPT -->
<script src="2b-template.js"></script>

<!-- (C) CUSTOM NOTICE BOX -->
<my-notice icon="info" text="Hello World!"></my-notice>

Notice the difference in this version? Of course, we used Javascript to build the shadow DOM in the previous example. But in this one, it is fully defined using <template id="noticetemp"> itself.

 

 

JAVASCRIPT – ADOPT TEMPLATE AS CUSTOM ELEMENT

2b-template.js
class Notice extends HTMLElement {
  // (A) CONSTRUCTOR
  constructor() {
    // (A1) ADAPT TEMPLATE INTO SHADOW DOM
    super();
    this._shadow = this.attachShadow({mode: "open"});
    this._shadow.appendChild(document.getElementById("noticetemp").content);
    this._wrap = this._shadow.querySelector("div");
    this._icon = this._shadow.querySelector("i");
    this._text = this._shadow.querySelector("span");
  }

  // (B) LISTEN FOR ATTRIBUTES CHANGE
  static get observedAttributes () { return ["icon", "text"]; }
  attributeChangedCallback (name, oldVal, newVal) {
    // (B1) SET ICON
    if (name=="icon") {
      this._wrap.className = newVal;
      switch (newVal) {
        default: this._icon.innerHTML = "X"; break;
        case "info": this._icon.innerHTML = "ⓘ"; break;
        case "warn": this._icon.innerHTML = "⚠"; break;
      }
    }

    // (B2) SET TEXT
    if (name=="text") { this._text.innerHTML = newVal; }
  }
}

// (C) REGISTER CUSTOM ELEMENT
customElements.define("my-notice", Notice);

Look no further, this is pretty much the same as the previous method. Except that we create the shadow DOM by adopting the HTML template – this._shadow.appendChild(document.getElementById("noticetemp").content).

 

 

USEFUL BITS & LINKS

That’s all for the tutorial, and here is a small section on some extras and links that may be useful to you.

 

WHICH IS BETTER – JAVASCRIPT OR HTML TEMPLATE?

Personally, I am leaning towards pure Javascript – It is better in the sense that we can package and contain our custom element in a single file. HTML template works too, but the template has to go somewhere…

 

LIMITATIONS & POLYFILLS

As at the time of writing, Web Components and Custom Elements are already well-supported by most modern browsers. But if you have to support browsers that don’t, you will have to use a Polyfill – Check out webcomponentsjs.

 

INFOGRAPHIC CHEATSHEET

How To Create A Custom HTML Element (Click to enlarge)

 

LINKS & REFERENCES

 

THE END

Thank you for reading, and we have come to the end. I hope that it has helped you to better understand, and if you want to share anything with this guide, please feel free to comment below. Good luck and happy coding!

Leave a Comment

Your email address will not be published. Required fields are marked *