Custom elements are the answer to modernizing HTML; filling in the missing pieces, and bundling structure with behavior. If HTML doesn't provide the solution to a problem, we can create a custom element that does. Custom elements teach the browser new tricks while preserving the benefits of HTML.

A simple component

Web Component is an umbrella term that describes a collection of four technologies that help making reusable components. This example will only touch on 'Custom Elements' but for completeness the other three are:

  • The <template> tag that is used to define inner HTML that is not rendered to the page but can be used as, you guessed it, a template for components. It is often useful for components that need to render a complex inner structure. Instead of creating the DOM by hand the content of the template can be copied and inserted into the component.

  • The Shadow DOM which is a DOM subtree that is hidden from the rest of the document. CSS rules defined inside the Shadow DOM won't affect anything outside of the subtree and vice versa. This helps with issues where CSS rules would leak and alter nodes that were not intended to be styled.

  • HTML imports is a way to include HTML documents in other HTML documents. You're not limited to markup either. An import can also include CSS, JavaScript, or anything else an .html file can contain. In other words, this makes imports a fantastic tool for loading related HTML/CSS/JS.

The Custom Element API

The Custom Element API gives us the ability to register new HTML tags and extend the HTML language with new elements.

customElements.define(tagName, cls)

In order to avoid conflicts with future native HTML elements only tag names that contain a hyphen can be registered. Attempting to register a tag name without a hyphen will result in the following error in Chrome:

  Uncaught DOMException: Failed to execute 'define' on 'CustomElementRegistry': "myelement" is not a valid custom element name(…)

It is recommended to only use hyphens and alphabetic characters in tag names, but digits are also allowed. Some other restrictions apply such as not being able to register tag names that start with a digit/hyphen or that contain special characters.

The functionality of a custom element is defined using an ES2015 class which extends HTMLElement. Extending HTMLElement ensures the custom element inherits the entire DOM API and means any properties/methods that you add to the class become part of the element's DOM interface. Essentially, use the class to create a public JavaScript API for your tag.

  customElements.define('my-element', class extends HTMLElement {
  constructor() {
    super();
  }
}); 

A custom element can define special lifecycle hooks for running code during interesting times of its existence. These are called custom element reactions.

  • constructor An instance of the element is created or upgraded. Useful for initializing state, settings up event listeners, or creating shadow dom. See the spec for restrictions on what you can do in the constructor.

  • connectedCallback() Called every time the element is inserted into the DOM. Useful for running setup code, such as fetching resources or rendering. Generally, you should try to delay work until this time.

  • disconnectedCallback() Called every time the element is removed from the DOM. Useful for running clean up code (removing event listeners, etc.)

  • attributeChangedCallback(name, prevValue, newValue) An attribute was added, removed, updated, or replaced. Also called for initial values when an element is created by the parser, or upgraded. Note: only attributes listed in the observedAttributes property will receive this callback.

  • adoptedCallback() The custom element has been moved into a new document (e.g. someone called document.adoptNode(el)).

With this knowledge we can add some custom behavior to our component. We will create a simple component that displays a happy face when the happy attribute is set and a sad face when the attribute isn't present. Not a very useful component but it shows how the life-cycle callbacks can be used in a few lines. (more info available here)

This template can serve as a starting point to create more complex and styled custom elements like a dropdown panel, an app drawer or even a custom slider making them easy to reuse. The most important part is understanding the lifecycle hooks and what logic should be placed in each.


1,706 0 22