What are Custom HTML Elements?
Tags: vue, customelements, webdev, javascript
Published: 2019-07-06 13:23:09

As I've been looking at how to structure my JavaScript in my applications using modern JavaScript frameworks, I keep running into the same issue. I want to use component-based design, but I'm not always looking to build a Single Page Application. Sometimes, I just want to add a component onto a page that isn't tied to the framework at all.

In a framework like Angular, that's fairly impossible. I've always seen Angular as an all or nothing framework. It's either all Angular or none of it is. And if I have a legacy application that I want to upgrade little by little, all or nothing isn't what I'm looking for.

[Vue](https://vuejs.org/) labels itself as an "incrementally adoptable" framework for modern JavaScript components. I've found this to be generally true but I've been struggling a bit on how best to incrementally adopt it. Do I just start creating Vue instances and put all my JavaScript in there? Do I build components and then try to figure out how to get that started with my current big application? How do I not load all the components for every page when my pages only use a small subset of components? Does that mean I have to give up my idea of a global JavaScript file and now create a JavaScript file for each page? And isn't that what I'm trying to avoid?!?

When playing around with the [Vue CLI](https://cli.vuejs.org/), I noticed that there was an option to build the Vue components as native web components. Trying to figure out what that means, I dove into the documentation.

## Web Components Standard

[The Web Components Standard](https://www.webcomponents.org/specs) is a standard that's been adopted by the major browsers as **a way for developers to create their own HTML elements**.

This may sound strange at first and not very useful. Why would we want to create a new `<div>` or `<p>` tag? But there are some HTML elements that have a lot of useful functionality behind them, like `<video>`, `<canvas>`, and even the `<select>` element.

`<select>` is actually built with a number of cool features. By default, you can define a select box and a number of `<option>`s and the browser knows to show that to the user as a dropdown selection and to limit the user to only selecting one. If you pass the attribute `multiple`, the functionality changes to allow the user to pick more than one option and displays the control as a scrollbox rather than a dropdown.

This shows just how complex simple components can be. They have structure (a `select` contains `option`s), presentation (shows as a dropdown or scrollbox), and logic (user input is validated on whether they can select multiple options or not). This functionality sounds a lot like JavaScript components!

## Constructing a simple custom element

If you know how to build a component in Vue, you already know how to build a custom HTML element, just make a new standalone Vue component!

Since Vue components are used as if they were HTML elements, if you build a standard single file component in Vue, you can export that as a new HTML element from your project.

I hope to walk through this process in another post, but we can take a look at a quick example now.

I remember the wonder years of 1999 web development. Anything was possible then, including the wonderful `<blink>` tag. Sadly, every browser killed this poor tag some time ago, but we can bring it back in fashion!

Below, I define a new component that will take whatever is between its tags and blink it. Just like the good ol' days!

```html
<template>
  <span ref="blinkyText"><slot></slot></span>
</template>

<script>
export default {
  name: "blink",
  mounted() {
    setInterval(() => {
      this.$refs.blinkyText.classList.toggle("onoff");
    }, 500);
  }
};
</script>

<style>
.onoff {
  visibility: hidden;
}
</style>
```

I can then compile this into a [custom HTML element using the VueCLI](https://cli.vuejs.org/guide/build-targets.html#bundle-that-registers-multiple-web-components) and deploy it to any old webpage with something like this:

``` javascript
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.8/dist/vue.js"></script>
<script src="/js/my-blink.min.js" defer></script>

<my-blink>Party Like It's 1999!!!</my-blink>
```

This tag is decoupled from any framework or other JavaScript. You don't need a `main.js` or anything else to mount this tag or get it started. You just include the Vue JavaScript file, your tag JavaScript file, and then use the tag like any other HTML tag!

## Future articles will cover more

I'll eventually write up a couple of articles around how to build this style of Vue component and how to tie it into Vuex to allow these components to talk to each other. Here are some that are already done:

- [Setting up a Vue CLI project for building custom HTML elements](https://jerickson.net/setting-vue-cli-project-custom-html-elements)
- [Building custom elements from Vue CLI](https://jerickson.net/building-custom-elements-vue-cli)