Easy apps with hyperHTML — 5, Custom elements
Tags: javascript, hyperhtml, tutorial, webdev
Published: 2019-05-31 14:50:46

[Version en español](https://medium.com/easy-apps-with-hyperhtml/aplicaciones-f%C3%A1ciles-con-hyperhtml-5-402970bf26b4)

Part 5 written by {% user pinguxx %} {% user paritho %}

1. [Introduction, wire/bind](https://dev.to/pinguxx/easy-apps-with-hyperhtml-1-31cc)
2. [Events and components](https://dev.to/pinguxx/easy-apps-with-hyperhtml-2-hbi)
3. [Moar about components and simple state management](https://dev.to/pinguxx/easy-apps-with-hyperhtml-3-1m3l)
4. [Wire types and custom definitions (intents)](https://dev.to/pinguxx/easy-apps-with-hyperhtml-4-38k3)
5. **Custom elements with hyper**
6. [Customizing my custom elements](https://dev.to/pinguxx/easy-apps-with-hyperhtml-6-1e73)
7. [Testing!](https://dev.to/pinguxx/easy-apps-with-hyperhtml-7-5b9g)
8. [Async loading, placeholder and a Typeahead with hyper](https://dev.to/pinguxx/easy-apps-with-hyperhtml-8-async-loading-5d96)
9. [Handling routes](https://dev.to/pinguxx/easy-apps-with-hyperhtml-9-routing-59k0)
10. [3rd party libraries](https://dev.to/pinguxx/easy-apps-with-hyperhtml-10-3rd-party-libraries-2n7b)

![](https://thepracticaldev.s3.amazonaws.com/i/rgz0njhgpaofeptcv3ra.jpeg)

### My very own tag

If you’ve been developing web pages or apps for any amount of time, chances are you’ve seen custom components or HTML tags, especially if you’ve worked with one of the newer JavaScript frameworks. Whatever the name, these components usually all have a similar goal: encapsulate functions and logic into re-usable chunks of code. After a lot of discussion and development by the community, we finally brought these ideas under the canopy of [Web Components](https://developer.mozilla.org/en-US/docs/Web/Web_Components).

> Web Components is a suite of different technologies allowing you to create reusable custom elements — with their functionality encapsulated away from the rest of your code — and utilize them in your web apps. — MDN

In order to achieve this, the specification addresses the following technologies:

1. **Custom elements** : A set of JavaScript APIs that lets you define the logic of the element. You can create a function like open() and then you can just call that function from the element document.querySelector(‘custom-dialog’).open().
2. **Shadow Dom** : Utilizing the shadow DOM enables you to use other HTML elements inside your custom element, even other custom elements! Whatever code is inside the custom element with shadow DOM enabled will be rendered and styled separately. This can be both a bug and a feature, depending on your use case.
3. **HTML templates** : Two tags `<template>` and `<slot>` let you write HTML inside the custom element, but they are not rendered or displayed until you activate them. `<template>` and `<slot>` can also be re-used for other things.

Not every one of these are ready to use in all browsers, and certain aspects are harder to polyfill than others, but we are getting close.

For now we will concentrate on the basics of custom elements, but at the end we’ll show you how to use the shadow DOM. We’ll also see that hyperHTML can create highly efficient templates. We’ll be able to do everything with hyperHTML that `<template>` can, except for native use of `<slot>`.

#### \<custom-element awesome=”true”\>

To get started making custom elements, we need need 3 things:

1. A browser that supports custom elements, or a polyfill. We recommend using [document-register-element](https://github.com/WebReflection/document-register-element). This polyfill has been used in production for a while by projects like amp. Alternatively, Google also has a polyfill worth checking out.
2. A class that extends the HTMLElement object. This class will contain all the logic for our element. We can also extend from a normal HTML element.
3. Finally, we will need to “define” the element. This is how we’ll register our element in the browser, and give it a name.

In 2 above, we mentioned that there are two ways to extend your custom element class, either by extending HTMLElement, or a build-in HTML tag like span. When we extend HTMLElement, our custom element will be autonomous. However, extending built-ins is a bit controversial, and while the specs describe how to do it, not all browsers are on board. We will see how do both, but for our use case in this blog we are only going to extend from HTMLElement.

Enough jibber-jabber, show me the code!

{% stackblitz intermediate-ce-01 ctl=1 %}

(A tiny, huge thing in the index.html, since Stackblitz is automagically transpiling our code, we need to include the es5-adapter)

Lets dissect the code a bit:

First we name our class and extend HTMLElement. This will be an autonomous element. Then, we have a function called `connectedCallback()`. `connectedCallback()` will be called after the browser has appended the custom element to the document. We’ll talk more about the custom element lifecycle later on. Inside `connectedCallback()` we are just adding text to our custom element.

On line 9 we are defining our element name. Whatever name you choose, you must include a hyphen. In our case, to use this element in HTML we will use the tag `<custom-element></custom-element>`. The define function also takes a second parameter which is the class describing the element.

Internally, the browser keeps a registry of all the custom elements we define. One of the things we can do with the [CustomElementRegistry](https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry) is wait for the elements to be defined. However, we can do a lot of other things. So be sure to take a look at the docs.

![](https://cdn-images-1.medium.com/max/325/1*Uf6yUZuRXXKeYHFL8jabYg.jpeg)

Just for funz lets see how to extend a built-in:

{% stackblitz intermediate-ce-02 ctl=1 %}

#### Life cycle

Besides `connectedCallback()` there are other lifecycle hooks we can use. Let’s see what they do:

- **connectedCallback** : called when your element is connected to the document DOM.
- **disconnectedCallback** : called when your element is detached from the DOM. You will be able to react to that disconnection in this function.
- **adoptedCallback** : is called when you move the element to a new page.
- **attributeChangedCallback** : this is called whenever attributes that you are monitoring change. Not every attribute on your custom element will automatically be monitored. To pick which attributes are, you will have to add another function:

```
static get observedAttributes() {return ['attr1', 'attr2']; }
```

Let’s see these functions in action in our custom-element:

{% stackblitz intermediate-ce-03 ctl=1 %}

This is cool! We’re well on our way to having a powerful new element. But what about templating? With hyperHTML, we can render whatever we want inside the element and boom, custom elements with all the power of hyperHTML :D

![](https://cdn-images-1.medium.com/max/220/1*oQV46mV2KLACQJMUNZ6WjA.gif)

Let’s make a few changes. Of course, we first need to load hyperHTML. Then we will bind to the current element with `this.html = bind(this);` We also need a `render()` function which will be charged with rendering the template. And finally, we need to update our `attributeChangedCallback()` to change new attribute values to actual properties:

{% stackblitz intermediate-ce-04 ctl=1 %}

### Enter hyperHTML-element

There is a lot more to learn about custom elements, but fortunately the master [Andrea Giammarchi](https://medium.com/u/cc83da4b8256) created another utility that abstracts much of the custom element API. It’s called [hyperHTML-element](https://github.com/WebReflection/hyperHTML-element).

This is a class that you can extend instead of the HTMLElement, and it gives you a few new functions and properties.

#### Functions

- Besides the regular `observedAttributes` we now also have `booleanAttributes`. This is where we’ll want to put the attributes that don’t need an actual value. For example, `<custom-element required>`. “Required” either exists or it doesn’t, so it is a great candidate for being a boolean attribute.
- `observedAttributes` and `booleanAttributes` both will also define getters and setters for every attribute observed. This means we no longer need to do `this[name] = newValue` inside attributeChangedCallback.
- `created`. This is a new callback that will be invoked right before `connectedCallback` or `attributeChangedCallback`
- `defaultState()`. Whenever you use this.state, the `defaultState()` function returns an object that becomes, you guessed it, the default state.
- `setState`. Updates the state object (you don’t need to define `defaultState` to use `setState`). `setState` includes a little bonus: it will call `render()` for you, so your element is automatically updated when you change its state!
- `render()`. This function should return the HTML you want to display inside your custom element.
- Event handlers. In your template, we can now pass ‘this’ to our event handlers: `<div onclick=${this}></div>`. If we then define an `onclick` function inside the element, it will get called with the proper context. You can also use data-call to call any function: `data-call=onAnyEvent onclick=${this}…` in this case `onAnyEvent` will be called when a user clicks. Even more cool, custom events are super easy: `onMyCustomEvent=${this}` and you define a `onMyCustomEvent(e)` function, done!
- `define()`. This static method is just a helper. Instead of using `customElements.define` you use this one `MyElement.define('my-element')`
- All the hyperHTML functions, `bind`, `component` and `wire`. The only hyper function we don’t get is define for intents, since we’re already using it to register the element. To define intents you will use `HyperHTMLElement.intent(…)`. We looked at intents in another [part 4](https://dev.to/pinguxx/easy-apps-with-hyperhtml-4-38k3) of this blog, so be sure to brush up on them if you need to.

Observant readers will notice some of these functions are used in `hyper.Component`. Hopefully you are familiar with some of them now :D

#### Properties

- `this.html`. In our example above we did `this.html = bind(this);` in the `constructor`. With hyperHTML-elemet we now can use `this.html` directly.
- state. State is an object that holds the…er…state of the element. It’s empty by default, or it will hold whatever object is returned by `defaultState`, if you’ve defined that function.
- As mention before, every attribute in `observedAttributes` and `booleanAttributes` will have its getter and setter, so you can use `this.attr` and the value will reflect the attribute. For example, `<my-element parent="your mom"/>`, using `this.parent` will give you the value, “your mom.”

Sweet! Let’s update our previous element to use all of these new features:

{% stackblitz intermediate-ce-05 ctl=1 %}

![](https://cdn-images-1.medium.com/max/500/1*TTLxQoccKUZvMDwsgPJO1A.gif)

What about the shadow DOM?… oh yeah, it’s super easy:

{% stackblitz intermediate-ce-06 ctl=1 %}

Check line 8 `this.attachShadow({mode: ‘open’});`, if you inspect the element you’ll see that the shadow DOM is there!

![](https://cdn-images-1.medium.com/max/466/1*Vtc8-XOraDyHOO5Ar741Rg.png)

There is more to learn about custom elements and web components in general. In the next part we will write some custom logic and update our table from [part 4](https://dev.to/pinguxx/easy-apps-with-hyperhtml-4-38k3) to be a custom element. And as always, feedback is welcome!

* * *