My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.

Tuesday, July 15, 2014

A W3C Custom Elements Alternative

This W3C specification is awesome, and if you follow Alex Russell you know this has been probably his biggest obsession for years now: custom Web components, something even Internet Explorer 5.5 had, thanks to its Element Behaviors capability!

Many Articles, Poor Support :-(

Regardless the amount of articles about Web Components and a dedicated website for Custom Elements, browsers capable of supporting them natively are ... well, most likely none of them, specially on Mobile world.
This has at least 2 side effects:
  1. delayed adoption and/or interest from developers
  2. need for performant, not obtrusive, and reliable polyfills
Unfortunately, today there's no such thing ... like a plural of choices, and even Mozilla x-tag project, when it comes to Custom Elements, is based on the only polyfill you can find out there, the one proposed by Polymer framework ... however ...

Polymer Is NOT Standard .

Every developer I've talked about Custom Elements replied some how like "yeah, Polymer, I've heard about it ..." ... well, the bad news is that if you think about Polymer you have no idea about the proposed standard.
True is that the entire Web Components and Custom Elements "affair" comes from Google, and in the Custom Elements case, Dimitri Glazkov, a very nice dev that already helped me with few hints, is the main specs editor.

The Misleading Piece

Unfortunately most web articles about Web Components and Custom Elements end up talking or pointing at Polymer platform but this is indeed a platform a part, same as X-Tag, Ember or Angular would be, it's not the standard itself!
As result, most example you can read online are not exactly what W3C is proposing, rather what Polymer platform provides!
Also bear in mind, Custom Elements does not mean Web Components, the proposal is huge and we should rather learn more and be able to distinguish between these standards.

Custom Elements As In Polymer

The provided polyfill is a very nice piece of code, well documented, I believe well tested, but unfortunately incomplete.
As example, if you follow its building instructions you will end up requiring an extra MutationObservers polyfill that as first line assumes you are in ES6 and the WeakMap constructor is available.
The amount of code required to provide a proper WeakMap polyfill in engines where WeakMap is not supported results into an overbloated, probably unnecessary, and not so welcome entire new library to the plate ...
... and then there are web developers that actually care about code size and compatibility with not that old browsers too ... not willing to bring 2 third parts libraries in order to better support this brand new API ...

A Lightweight Alternative

During last week I've had a chance to experiment with some sort of cross domain Custom Element and I won't bother you with details since I'd like to share instead the library that only today I've managed to organize in a repository and test with all devices and browsers I could: let me introduce you my W3C Custom Elements Polyfill, and these are few features:
  • less than 2KB minified and gzipped without extra dependencies, you serve this file, you have it!
  • a wide range of old to modern mobile devices support ... iOS 5 and Android 2 are only few of them, IE9 for Windows Phone 7 made it too together with webOS 2!
  • focus on one task ... and while this sounds obvious, I don't think to use Custom Elements we need a proper, partial, cross browser fix for both MutationObservers and WeakMap ... but good news is, if you have already a patch for MutationObservers these will be used instead of old Mutation events API: it's a win-win!

Try It Yourself!

Not only you can test it directly, you can also read most famous articles and experiment just including the single file.

As usual, contributions are welcome, and I'll put your name in the MIT license too, no CLA required ;-)

18 comments:

Unknown said...

Hi! Thanks for your article!

Just one thing: I think it'd be fair if you would at least mention that your custom element polyfill is really just about registering custom elements on the DOM. So it's not really a comparable lightweight solution nor alternative to Polymer or X-Tags, because it doesn't handle HTML Templates or Shadow DOM (what platform.js actually does and is used by Polymer and X-Tags).

However, nice article though! I like it how you make clear that Polymer is not *the* web component standard but rather a layer that sits on top of the actual specs.

Unknown said...

> Polymer Is NOT Standard .

What about Bosonic project ?
How did you miss that http://bosonic.github.io/

Andrea Giammarchi said...

Pascal you made the mention within you comment ;-)

However, saying "is really just about registering custom elements on the DOM" is unfair since the execution of created, attached, and detached callbacks, together with attributeChanged one are the main reason Custom Elements are AWESOME, these callbacks are what make the standard portable and lightweight making runtime creation of interfaces and their binding straight forward.

Although, I agree the rest brings even more to the plate but hey ... one polyfill per time ;-)

Maxime, I've missed that because I didn't know about it. Let me know if this polyfill works out of the box there too, thanks

xxgreg said...

You can build the custom elements polyfill from polymer separately from shadow dom, templates, etc. From memory it's only about 10kb.

Andrea Giammarchi said...

xxgreg just no, CustomElements is 15KB and it misses WeakMap plus it works only with MutationObserver ... nobody talked about Shadow DOM ... please read this post, and eventually the repo too, to know more why I didn't just waste time here ... take care

xxgreg said...

I have read the post, and re-read it, but I am still not clear on exactly which problem this new polyfill solves, and how it is different to using the existing custom element polyfill.

The mutation observer polyfill is 9KB, and is not required in Chrome/Firefox/IE11+/Safari.

http://caniuse.com/mutationobserver

So you're right the polyfill is required for IE9 and IE10.

In these browsers it depends on a weakmap polyfill, how many kb is that? Have you found this to be a problem in practice when targeting IE9/10?

Another question, in the polyfill implementation, how do you polyfill custom element lifecycle events without using mustation observers?

I have written quite a lot of code using custom elements directly, without polymer/shadowdom etc. This is a very good workflow for my use case. So I'm interested in this polyfill, which is why I'm asking these questions.

Andrea Giammarchi said...

you didn't ask a thing in your first post, you said something that is both wrong, since I've already written CustomElements is at least 15KB without WeakMap and requires build process while this doesn't and is about 13KB all included.

So what was your point there?

What is the point here is that this is an alternative and a one everyone can contribute without signing a CLA, which is already by itself a good thing.

If you were happy with what Polymer was giving you already why are you here asking questions you don't want to hear answers?

WeakMap ... a reliable one is linked already, you go and ask M.Miller how much code is that because to me other polyfill for WeakMaps are not a real option, the lighter one attach properties at runtime to any object you pass on it ... so no, I won't use that, but with that thing in would be at least 16KB, already too much and with 3 different libraries imported instead of just one focused on one thing and for this reason also performant.

So here my question: do you have actually something concrete that you would like to know or some bug you would like to report?

Thank You

Andrea Giammarchi said...

P.S. for the bug ... pull requests are welcome, and reports in github please

xxgreg said...

So the polymer polyfill plus dependencies required for IE9/IE10 is ~40kb, and other browsers ~15kb.

It sounds like the one you have built is definitely smaller.

Happy hacking ;)

Unknown said...

Thanks for sharing, Andrea!
This couldn't come at a better time. I am researching the application of custom elements for a large webstack, and this pretty much comes as a blessing.

One question though:
Why this format:
createdCallback: {value: function() {

Instead of:
createdCallback: function() {

I can't find clarity in neither the spec or your source.

And thanks agsin. :)

Andrea Giammarchi said...

that's how you create properties through descriptors in JS under ES5+

you have two options here:

1. just create the proto object a part then add methods as you usually do ... `proto.createdCallback = function(){};` ...

2. use some light utility like this one that lets you do a simpler, inline,

create(HTMElement.prototype, {
createdCallback: function () {}
})

as you would do normally with enumerable, writable, and configurable properties.

Victor Nystad said...

Excellent work, thanks for sharing :) I definitely prefer your solution to Polymer or X-tags or even Bosonic. I’ll use the full web components stack when we don’t need insanely complicated polyfills anymore, until then I’ll stick to custom elements.

DanMendes said...

Hi Andrea, really great work. But could you provide a more complex example? For instance if we want to create elements with attributes like

Andrea Giammarchi said...

here you have two clocks, one without attributes, and one with attributes.

the post is here, feel free to check source code and reuse it if neded

Dave S. said...

Thanks for the work on this Andrea. I just wrote an angular add-on that sits on top of this for defining elements then auto binding the properties.

angular-custom-element

Andrea Giammarchi said...

I'd love to see/know more !!!

DM said...

How do you polyfill custom element lifecycle events without using mutation observers?

Andrea Giammarchi said...

DM there are fallbacks and tests and real-world examples already, which problem you think you might have, exactly?