In a soon-to-be-released project, I decided to give web components a chance. Instead of using frameworks like x-tags or polymer, I went with the plain ol’ vanilla JavaScript web component APIs with little to no external dependencies.

Browser support for web components is pretty good, and with the use of webcomponentsjs polyfills, I was able to get support for every browser we currently support in ONTRAPORT.

Along the way, I made a few discoveries. This post assumes that you have some knowledge of the APIs that make up web components. If you don’t, I would recommend reading:

Now any <my-button></my-button> on the page will render with red text. Say you wanted to create a new element that extended from <my-button>. If it’s contained in the same script file, you can grab the variable prototype, create a new prototype, and start from there.

But say you want the extending element that lives in another import or script file. We have no way to easily grab the prototype from memory.

When I first started playing with this API, I was under the assumption that you could access your custom elements prototype via document.registeredElements[‘my-button’].prototype. Turns out that’s not the case. In the end, I had to build a small script to track our custom element:

Earlier in this post I mentioned that I didn’t use a framework, but undoubtedly some of you consider this a “framework.” It turns out, with a bit of hacking, you can get the element constructor of an element via document.createElement(‘my-custom-element’).constructor. From there you can create a new element that extends my-custom-element. I don’t think that’s ideal because you’re creating a new instance of an element that you have no intention of using. If that element had a “heavy” setup method, you could be taking on some performance issues.

2) document.currentScript.ownerDocument

I think two of the most important and powerful parts of HTML imports are:

document.currentScript

and

document.currentScript.ownerDocument.

document.currentScript refers to the current <script> block being processed.

document.currentScript.ownerDocument refers to the page that “owns” that block. Take the following code block as an example:

By using _currentScript.ownerDocument I have access to the whole HTML file. This is very powerful, because it allows us to grab only this component’s template(s), images, SVGs and other resources, and then inject them into the DOM or reference and manipulate them from within the script.

We could traverse into icon.html and access its contents via: _currentScript.ownerDocument.querySelectorAll(‘[href=”icon.html”]’)[0].import.

I’ve seen examples where the _currentScript is attached to the document, and that doesn’t work because if you have two components doing:

document._currentScript = document._currentScript || document.currentScript;document._currentScript will always reference the currentScript of the last component added. That’s because document refers to the main window, not the html import document.

3) Projecting Content With ShadowDom Is Somewhat Limited

The ShadowDom is very powerful and more so when used in conjunction with a template tag that contains a content tag. For example:

However, the input’s name attribute will still be name=”<content select=”.inputName”></content>”, which is a huge downer. Turns out I can’t project any content into an element’s attributes. If I was able to, it would be insanely useful for building custom form controls.

This isn’t a show stopper. A quick and simple workaround might look something like this:

Wrap-up

I still don’t regret not using polymer or x-tags; it allowed me to better understand the APIs that make up web components. Also, it has added benefits for ONTRAPORT and the Engineering department:

I won’t get stuck on an unsupported version of a framework or library because I’m using the APIs that the browser supplies directly.

I can take my generic elements and drop them into any site / app regardless of the framework they’re using and have them work with little to no extra work.

It cuts down the learning curve before engineers and designers can start working in our app because the end result is working with html.

So whatever framework you use, check out vanilla web components! You might like working with html again!

About Jesse Baird

Jesse Baird is a Senior Software Engineer at ONTRAPORT. A former organizer of IowaJs, Jesse likes to discuss open source software, best practices and the latest web standards on jebaird.com and @jesse_baird. When not online, Jesse can be found in the great outdoors, grilling a steak over charcoal or out running.