JavaScript decorator for custom elements

Custom elements are around for quite some time, but after watching the latest episode of "Supercharged" which is about building a router component with custom elements, I felt the urge to fiddle around with it, too. To create a custom element with today's modern APIs, there's still some boilerplate code to write. And because I want to try out JS decorators as well, I build a simple decorator for classes which reduces the amount of this repetitive code a little bit.

In this post I will shortly present its functionality and talk about the experience while developing this component.

What's it for?

When you want to create a custom element, one option is to extend HTMLElement as a subclass. It could look like this:

export default class SuperElement extends HTMLElement {
  someMethod() {}
  ...
}

To be able to use this element, you need to make sure that you pick a valid name and register it along with the constructor function as an element in the DOM. For the previous example it can be done in this way:

document.registerElement('super-element', SuperElement);

These tasks, finding a valid name and register the element, are covered elegantly by the decorator.

Installation

The easiest way to install the decorator is by using npm.

npm i custom-element-decorator --save-dev

Please note that JavaScript decorators are not in included in ES2015/ES2016. Currently, they are in stage 1 as an active proposal for ECMAScript, so you probably a transformation step for it (e.g. a babel plugin).

Usage

Once installed, make sure that you import the customElement-decorator in your code. Then you can use it by prepending it to the class which extends HTMLElement.

import { customElement } from 'custom-element-decorator';

@customElement()
export default class SuperHotComponent extends HTMLElement {}

By default, the decorator tries to guess the name for the custom element by converting the 'camelCased' classname into a dashed one. Referring to the example above, a component with the name super-hot-component will be registered.

It is also possible to explicitly set the name for the custom element via the decorator. Simply put the desired name as an argument into the decorator expression.

import { customElement } from 'custom-element-decorator';

@customElement('super-cool-component')
export default class SuperHotComponent extends HTMLElement {}

Regardless if explicitly set or guessed, the name is checked for validity by Sindre Sorhus' awesome 'validate-element-name' module.

Testing custom elements with Node.js

As a test runner I chose mocha because of its good documentation and many plugins. One thing which was kind of quirky is the testing of decorators. To keep the tests simple and small, I did not integrate any browser environment like phantomJS. Though in order to have reliable tests, a browser environment (API) needs to be simulated at least.

A good library to achieve this goal is jsdom-global. 'Jsdom' is a 'JavaScript implementation of the WHATWG DOM and HTML standards' which you can use with Node.js and jsdom-global, helps you, as the name suggests, to make the DOM API and browser variables like document or window globally available. This helps a lot simulating a browser environment in Node.js, especially for testing.

Unfortunately, the custom elements spec is not officially supported with current jsdom releases, so we need to make use of a polyfill. I can recommend the 'document-register-element'-module, which is a lightweight polyfill for the W3C Custom Elements spec.

To keep our tests clean, we can instruct mocha to require these two modules at start. I put these run configurations into a mocha.opts file:

--recursive test/**/*.js -r jsdom-global/register -r document-register-element

Now it is possible to use the custom element APIs within a test like if you were in a browser!🚀

Wrapping it up

To my mind JavaScript decorators and Custom Elements are pretty cool features which are coming to web developers. Decorators are a powerful pattern to make the code more readable and they are easy to develop and use. If you already have Babel in your build pipeline, you should consider adding the decorator-plugin to your project and give it a try. Personally, I hope that this feature will come to ECMAScript soon.
Native Custom Elements are awesome as well and easier to develop as I thought, but it's questionable if they become accepted in the near feature, because the 'big' frameworks like React, Angular or Polymer already give developers the opportunity to create custom elements/components integrated with other battle-proven features.
Though, if you targeting only modern browsers and don't need a entire framework, it's kind of refreshening to write custom elements natively :)

Do you have other experiences? Feel free to drop a comment below this post!


Useful resources

Comments on this post


comments powered by Disqus