Extending Built-in Elements
Looking at the history of Custom Elements we can analyze few facts:
- the initial momentum, brought by Chrome and advocated through Polymer, was based on V0 API and also built-in extends
- most successful stories, including AFrame or AMP, were based on V0
- when V0 got killed, V1 discussed, and built-ins re-factored-out from Polymer, developers still believing in standards understood the feature was not stable and started looking at every Web framework that meanwhile solved through built-in elements all major devs painful points
Even if agreed between more browsers vendors, the V1 API left web developers with tons of issues that were previously, somehow, solved:
- classes such
HTMLInputElement
started breaking once extended - the
createdCallback
got removed, leaving developers incapable of finding, in a reliable way, an entry point to simply initialize their components - what was well understood and advocated as progressive enhancement, through built in extends, became a nightmare full of issues for transpilers, polyfills, older browsers, etc. Everyone had to re-implement the wheel through a meaningless
HTMLElement
base class that was bringing in nothing different than what aMutationObserver
could’ve done already - everything problematic on the Web remained the same, but new not-polyfillable “features” such ShadowDOM landed on developers’ shoulders
To me, V0 API was the classic example that pragmatic is better than perfect, but also that pragmatic works (and V1 is everything but perfect anyway).
Instead, in 2018, the most basic/needed specification out of Web Components is still under development and quite fragmented beside few exceptions:
- Chrome has native
customElements
with built-in support - Chrome also still has V0
document.registerElement
around - today Firefox has native
customElements
with built-in support - yesterday Firefox never had custom elements at all (or behind a “broken flag” full of hidden issues)
- AFAIK, MS Edge is developing Custom Elements with built-in support
- Safari, and WebKit based browsers, has native
customElements
since long time but without built-in support
…but what is that all the browsers have in common since ever?
Built-in Elements
Without even waiting a single day for custom elements to land on the Web, all successful, sometimes heavy, but super dev friendly frameworks, based their success on regular DOM elements such DIVs, BUTTONs, TABLEs, you name it.
Yes, once everything boils down to an over-polluted page full of DIVs that “just works”, one starts wondering if Web Components have ever been an answer or, if that’s the case, what is it that such specification exactly solved.
As of today, it surely caused an ulcera to my liver due the amount of time, stress, and discussions I’ve had since day 0 about this topic with other developers involved with this standard and its implementation.
I was so happy about CE potentials that I’ve even brought CEs down to IE8!
The mandatory Shadow DOM failure
I think Shadow DOM is the reason #1 Web Components never shined. Even if very good in theory, the reality about Shadow DOM is the following one:
- impossible to reliably polyfill (and super heavy/slow if you try)
- no way to simplify styles definition (discussed these days after years of “developers will figure it out”)
- the only way to date to simplify styles definition within the Shadow DOM is considered wasteful, slow, unnecessary overhead.
- if you add slots in the mix, it’s a whole new mess to find the right moment to understand how to deal with these, because the lifecycle of a Custom Element is known only at its opening, but nobody knows when the component is ready (discussed these days after years of “developers don’t need to know this”)
- as last nail in the coffin, Shadow DOM cannot be rendered on the server, hence neither delivered from the server
Accordingly, if you ever waited for Shadow DOM to land everywhere so you can start using Web Components, keep looking around at how much fun other developers are having instead, with their components solution based on regular built-in elements.
It’s literally that easy peasy lemon squeezy !
And more often than it should, that also makes me wonder if I’m even stupid in just waiting for standards to become a reality … I mean, in general …
It’s time to move forward with built-in extends
Since at least two major browser vendors decided that custom elements built-in extend are OK, and Chrome and Firefox cover the majority of the users, and since the only one strongly opposed to custom elements built-in delivers the fastest and most expensive Hardware out there (yeah, that’s Apple), so that a tiny patch won’t hurt at all in the real world, it’s time to move on and re-write the history of custom elements once again, this time doing what is the most natural thing to do, something that gives developers pseudo shadow DOM out of the box (input, button, table, select, checkbox), something that implements accessibility out of the box, and something that works from the most ancient browser to the most modern one, without fearing JS incompatibilities.
Strawberry on top, it’s something any Server Side Rendering can deliver.
The built-in element patch
Fully based on native customElements
implementation, built-in element is a polyfill that weights less than 1 Kb, it leaves Chrome and Firefox completely untouched, and it uses modern features on Safari, which is why it’s so lightweight.
All other old or not-there-yet browsers will also be unaffected, thanks to the document-register-element polyfill, the one that AMP and AFrame use too, which already provides custom elements and built-in capabilities.
<script>
if (!this.customElements)
document.write('<script src="//unpkg.com/document-register-element"><\x2fscript>');
</script>
Explained in my bloatless Web post, above technique won’t ever affect modern browsers neither and it will always work in old one.
And because document-register-element is based on ES3 compatible syntax, and since the eventually transpiled part of built-in-element would basically run only in Safari, or even Edge if they decide to not implement built-in for the time being, you can be sure this solution works from IE9 to latest Chrome, passing through even Android 2 phones, covering more than most FWs.
Is the button text blue? Can you click it? That’s it, you’re good to go.
The overhead is minimal for modern browsers, and identical for battle tested old browsers so that starting from now, every browser, and every server, can deliver custom elements built-in.
Yes, even your framework of choice 🎉
… but what exactly do built-in extends solve?
- when the component is ready, it’s alredy initialzied. Less code, less bootstrap, you focus as example on what an input does, not what it should contain and what aria attribute should use to be accessible and behave like an input. You use. a bloody. input.
- all built-in functionalities and methods work out of the box. You extend real built-in classes instead of extending
HTMLElement
only. All accessors will work as expected, and so everything else that’s natively there - all default styles are already applied.
HTMLDivElement
is already block,HTMLSpanElement
isn’t. - you have the same extendibility than you had before but it will work even with browsers that don’t support JavaScript
- you don’t create wrappers/containers for the sake of it, with the need to inject at distance nodes or Shadow DOM with nodes, you use the minimum amount of needed DOM nodes to deliver your project. That means lighter pages, lighter DOM trees, lighter Internet.
- you have a stronger way to style your components via
[is="my-element"]
without needing Shadow DOM. If you fear other selectors might be stronger deliver!important
all over and call it a day: it works!!! Otherwise add inline style: it works!!! Otherwise … well, you got it, there are various solutions, and none of them requires Shadow DOM polyfills.
… but I still want Shadow DOM
If you keep it simple … repeat with me … if you keep it very simple, you can forget heavy polyfills that are not super reliable and use simple solutions such attachshadow but I still suggest, for any other case, to consider using instead the built-in-element approach: that would rarely fail any expectation.