July 12, 2015

Using HTML5 right now: Web components by example (part 1 of 2)



In this article I’ll cover one of the most exciting and most useful parts of upcoming HTML5 - web components, and show you how to use them today already. This article features a complete example implementation using native HTML5 as well as some of the most prominent polyfill extensions.

DRY – don’t repeat yourself – is one of the most important clean code principles. Even the most simple, procedural programming languages typically support DRY compliant design, e.g. by encapsulating reusable code in functions. Technologies which abstract HTML views typically support the definition of reusable components as well; e.g. JSF features the so-called composite components, and AngularJS features so-called directives. With HTML5, creating custom reusable components will finally be natively supported by every implementing browser. Enter web components.

Its place in the HTML5 ecosystem

Web components are just one part of a multitude of features which are introduced in the HTML5 specification, the successor of HTML4 which is currently implemented by all major web browser engines. What is commonly referred to as “web components” are actually four different parts of the HTML5 specification:
For a basic overview about custom components, see webcomponents.org. Its resources section contains many useful artifacts such as hello world repositories and tooling support.

For an overview of current browser support, see Are We Componentized Yet?

In this article, I will concentrate on the central custom elements part of the specification.

An example problem with HTML4

For illustration purposes, I’m going to present a simple demo application of a web component. Even though the resulting HTML page can be perfectly created using pre-HTML5 technology only, it’s reusability would be clearly diminished, leading to impaired maintainability.

I didn’t have to look too far to find an example application of web components – there’s actually a good little use case on my blog. Consider the small “rating” widget I created for a blog post’s header section:
Rating
If we consider this widget as a component, it has exactly two variables:
  • The name
  • The number of stars
Everything else is the same and thus reusable. Repeating anything but these two variables in the source code would violate DRY. Let’s see how be can increase reusability with HTML prior to version 5.

Naïve, pure HTML & CSS

This is the first attempt:
<span class="my-infobox">Rating
    <i class="fa fa-lg fa-star"></i>
    <i class="fa fa-lg fa-star"></i>
    <i class="fa fa-lg fa-star-o"></i>
</span>
This clearly leaves much to be desired.
  • Good: The overall styling can be done in a central CSS file which will refer to the "my-infobox" class.
  • Bad: The overall syntax to include a rating is very bloated and requires repetition when used multiple times.
  • Bad: The “stars” must be placed imperatively (here, the stars are generated using the font-awesome vector icons library). This is error-prone and obfuscates the code.

More CSS

We could slightly improve readability like this:
<span class="my-infobox my-rating2">Rating<i class="fa fa-lg"></i></span>
Here, we define additional CSS rules: my-rating0my-rating3 which dynamically render the respective number of “stars”. Note that this is only possible with the somewhat hackish ::before and ::after selectors and by disrespecting font-awesome’s best practices to refer to an icon by its class name rather than by its character number. For instance, this generates the rating-2-stars:
.my-rating2>i::before {
    content: "\f005\f005"
}
.my-rating2>i::after {
    content: "\f006"
}
Unfortuantely, the basic problems remain:
  • The overall syntax to include a rating is still very bloated.
  • We cannot get rid of CSS classes declarations because our only way of abstraction is using CSS, and CSS declarations cannot refer to CSS classes; e.g. to get rid of "fa fa-lg", you’d have to extract the "fa fa-lg" class’s declarations and include it in a .my-infobox>i rule which of course would violate DRY as well.

The desired solution

What I’d like to have is a declarative, business case-driven custom HTML tag with a minimum set of busines-driven attributes, and with default values for attributes, like this:
<my-infobox stars="2"></my-infobox>
Well, this is exactly what we will end with using web components.

Library jungle or What the hell is a shiv?

Vanilla JavaScript for HTML5-compatible Browsers

If you target only browsers which natively support HTML5 web components (these are Chrome 36+ and Opera 30+ at the moment) you don’t need any external library at all. The JavaScript API functions required to implement web components are shipped with the browser.

Of course, this is the ideal situation associated with the least development setup effort and, more importantly, with the least deployment / bandwidth / runtime overhead. However, this will not apply to most projects which let the user choose any browser to his liking, however crappy it might have been programmed. In this situation, you have no choice but to fake browser support for web components by introducing a more or less thin JavaScript abstraction layer which provides the features missing in the browser core:

Fallback for older browsers: Polyfills shims / shivs

There are actually a couple of JavaScript libraries which emulate native support for HTML5 web components. They are all able to detect native browser support, and provide a fallback in case there is no native support.

These libraries are referred to as polyfills, and are an HTML5-specific implementation of what is known as a shim (literally a “washer”), which is sometimes also called a shiv. A shim or a shiv is just the general technical term for an API which retrofits an updated implementation into a legacy system; in the case of polyfills, it’s HTML custom elements retrofitted into legacy browsers.

The most basic polyfill available is webcomponents.js which is implemented strictly according to the W3C’s web components specification. The JavaScript API provided with this polyfill thus matches exactly the browser’s native JavaScript API, but it also comes with the most amount of boilerplate code.

All the other available polyfill libraries either build on top of webcomponents.js or recreated it and added additional functionality, mostly with the aim to reduce boilerplate code and increase overall ease of development.

Overview

Here is an overview of some of the most widely-used polyfill libraries.

Library C I T S IE FF Safari Andr iOS size Project health Download
(native) x x x x - - - 4.4.4 - - Implementation on hold in IE, FF, Safari -
webcomponents.js x x x x 10 x 7 x x 113 KB active,
23 releases,
35 contributors
bower / .js file
webcomponents-lite.js x x x 10 x 7 x x 36 KB active,
23 releases,
35 contributors
.js file
Polymer (based on webcomponents.js) x x x x x 7 x x 234 KB active,
63 releases,
55 contributors
.js file
X-Tag x x - - 9 5 4 2.1 ? 15 KB active,
70 releases,
10 contributors
.js file
Bosonic x x x x 9 25 6 ? ? 27 KB active,
0 releases,
4 contributors
bower / .js file (unminified)
document-register-element x - - - 8 x x 2.2 5.1 6 KB active,
33 releases,
3 contributors
.js file

All information is up to date as of the writing of this article (July 2015). C: Custom elements, I: HTML imports, T: Template, S: Shadow DOM. For polyfill libraries, compatibility data is taken from the respective project’s homepage. If there was no explicit version information, the column is simply marked with an “x”. The file size has been ascertained empirically.

On the next page, we will implement the example using the native JavaScript API as well as some of the most widely-used polyfill libraries.

Pages: 1 2