VGTech is a blog where the developers and devops of Norways most visited website share code and tricks of the trade… Read more



Are you brilliant? We're hiring. Read more

Light introduction to Web Components

CSS

Web Components is a new set of HTML5 definitions that is currently only partially supported in the latest browsers. This article intends only to glance at the technology hoping that you might get a little insight into one of the biggest evolutional steps of future HTML development.

So, what is Web Components? There is plenty of detailed resources covering the topic of Web Components. I will not go into details, just go through the general concepts and look at how combining the different technologies might make web development a lot easier and more effective in the future.

The first time I encountered parts of Web Components was when I looked at AngularJS to consider it as an alternative for Backbone.js as basis for a web application. I was instantly skeptical when I saw how they made their own tags (like <tabs> and <ng-form>) in their HTML. My experience as a web developer told me, this is ugly! (while some ASP-code flashed before my eies). Some time later, I actually read the theory, the technical side of Web Components and the status of the standards.

HTML Imports

Lets start with a simple, but very usable new function in HTML5. HTML imports simply enables you to include a html file from another one using a tag (or javascript). We have been including javascript and css since the dinosaurs walked the earth, so why should HTML be any different?

This is how it’s done:

<link rel="import" href="/path/encapsulated-code.html">

How hard was that? In addition, HTML imports support onload and onerror events, so if you are including third party code or just want better handling of the application flow, you can have event handlers for this.

I see several benefits of HTML imports. For instance, how many times have you included some javascript tool, jquery plugin etc. You have to include one or more .js files, and one or more .css files. Having one html file to include that contains all parts of the component you are including, makes the code more tidy in the application. Whoever offers this html (like the people behind the component or a local version for use in the applications made by your organization) can also change dependencies, upgrade versions (if backwards compatible) and other changes without having to maintain the implementation in every single application using the component.

This also make it easier to make encapsulated html components, like widgets or other blocks of code that can be used different places in your application, or across different applications. This gets even more useful when combining it with Shadow DOM and Custom Elements.

Browser support:

  • Chrome: 36+
  • FireFox: No
  • Internet Explorer: No
  • Safari: No
  • Opera: 23+

Polyfills for older browser exists

Shadow DOM

The basics of Shadow DOM is making encapsulated DOM. You can have a simple DOM-element (shadow host), create a shadow root based on that element and work on the content of the shadow root. Everything inside the shadow root is encapsulated, but on the screen, that content is displayed as if it was sub contents of the shadow host element. What you get then is content separated from presentation, something web developers have always wanted on some subconscious level, but has gotten used to living without.

In addition to just separating the shadow host from the shadow root, content can be injected into the shadow root from the shadow host (or from application scripts). To illustrate the full potential of this, let’s look at a simple example using the author information on this blog. Here is a screenshot:

author

 

So, how would HTML for this little block need to be? Here is a suggestion:

<div class="author">
    <img src="/path/to/avatar.jpg" />
    <p>Henrik Haugberg | Developer at VG | Structure the world, and make it look good (Frontend and Backend developer). Then disconnect from it all in the Norwegian mountains!</p>
</div>

Yes, I know. That is not nearly enough to make it look like the screenshot (unless you go crazy on pseudo-elements or something). The border is obviously made using an image, it has a fieldset-like heading (ABOUT the AUTHOR) and we will probably need a couple of wrapping tags to make the content look like that. Then of course, there is the CSS required for all this. And one of the great things about Shadow DOM is that the css is encapsulated from the rest of the document. No css is inherited from the host element or its parents.

Instead of extending the suggested HTML to include everything you need to make it look as intended, let’s hide all that in a shadow dom element, and the host element can just contain the important parts, the content. To achieve this, lets jump directly to the next part, making templates.

First, browser support for Shadow DOM:

  • Chrome: 25+
  • FireFox: 32+
  • Internet Explorer: No
  • Safari: No
  • Opera: 15+

Polyfills for older browser exists

Templates

Templates can be used for many purposes. In theory, they can replace most of the function of a modern javascript templating engine (however.. and so on). On the document level, they can be used to separate content from presentation using Shadow DOM.

Let’s continue the example from above. Instead of including all the details of how HTML templates work, I’ll make a super-simplified example of how a template for this example could be.

<template id="template-author">
    <style>
        fieldset { background: /***/ }
        .text { padding: 0.6em 0.4em; /***/ }
    </style>

    <script>
        /***/
    </script>

    <fieldset>
        <legend>ABOUT the AUTHOR</legend>
        <figure>
            <content select="img"></content>
        </figure>
        <div class="text">
            <content select="p"></content>
        </div>
    </fieldset>
</template>

And since CSS in Shadow DOM are complete encapsulated, you can style this as a completely standalone component, make it flexible and reusable for your application or even across applications.

Then, we need to apply this template on the host element(s). Again, a simple example. In an actual web application, this should probably be a part of a larger application structure.

<script>
    var shadow = document.querySelector('.author').createShadowRoot();
    var template = document.querySelector('#template-author');
    shadow.appendChild(document.importNode(template.content, true));
</script>

We are not only separating content from presentation, we are actually working on the DOM. This way of injecting templates on host elements is not making a string out of a HTML template or file and then injecting it with .innerHTML or $(..).html(), it actually happens on the DOM. It is simply a (future) standardized way of injecting templates into the document.

Custom Elements

With Custom Elements this becomes really interesting. Custom Elements is basically just the possibility of making your own tags and customizing how they work. There are many different ways of doing this and structuring the code, like before we will look at a very simple example.

Lets say we want to encapsulate the functionality of pagination. Pagination normally require quite a bit of html and css. Prev/next buttons, skipping pages when there is high number of pages but showing a couple of pages around the one you are one, adding a page link for first and last and so on. What if you could make a pagination block something like this:

<vg-pagination 
    href="http://myhost/path/mycategory" 
    current="3" 
    perpage="10" 
    total="114" />

Based on these attributes, a <template> with the corresponding script can then calculate what links to include, make the html, include the css needed and so on. It is also possible to override/inject css into Shadow DOM elements, so we can override the default styling of our pagination widget, like making it larger by overriding font-size.

A little bit of javascript is required to tell the browser how to treat this new tag, here is an example:

var VGPagination = document.registerElement('vg-pagination');

This actually makes an extended version of the default HTMLElement. There is also possible to extend other tags, like HTMLVideoElement. That would then inherit all the DOM functionality of HTML5 video tags, and you can add your own functionality on top of that.

var VgVideo = document.registerElement('vg-video', {
    prototype: Object.create(HTMLVideoElement.prototype, {
        // My custom video functions
    }),
    extends: 'video'
});

These elements can be instantiated the same ways you are used to. Like directly in HTML:

<vg-video>(...)</vg-video>

or in javascript

var myVideo = document.create('vg-video');
// or var myVideo = new VgVideo();
myVideo.addEventListener('click', function(e) {
    myVideo.play();
});

Custom element names must contain a dash. This “protects” the standard HTML tags, but also ensure backwards compatibility if new tags are added in future HTML versions. However, when custom elements extend standard elements with the extends-property (called type extension custom elements) like in the example above, these elements can be included with the original tag and an attribute telling the browser to use the custom element instead:

<video is="vg-video"></video>

Browser support:

  • Chrome: 33+
  • FireFox: No
  • Internet Explorer: No
  • Safari: No
  • Opera: 20+

Polyfills for older browser exists

The magic combination

The real magic happens when using these technologies to build up the whole structure of your presentation layer. Think about the evolution of backend programming the last couple of years. Moving from systems developed from scratch to fully featured frameworks. Modularizing functionality, reusability, open source package repositories in several programming languages. Backends (at least the widely agreed best practice way) consist of small, independent components and modules with specific purposes working together to make up the complexity of the system to fulfill the requirements.

Then what do we do on frontend? We build up a large spaghetti-soup of divs and other markup. The role and reasoning for how the markup are structured (including ids, classes and attributes) become a large, hard to maintain mix trying to ensure a combination of design and dynamic javascripts for ux and other functionality. From there we maintain and continue to develop on this over time, continuously changing every small parts of this, not always with full control of the complexity and context of why the initial decitions where made for everything from a class on the sidebar DOM element, to a property in a media query inside the css.

This is where Web Components can be used at its full potential. Both on the application level, within the organization and sharable components in the open source world. Components can structure your application better than before. Reusable chunks/widgets can consist of markup, encapsulated css and the necessary scripts for dynamic functions. Then the view-templates for your application can just consist of the content and content structure.

Think about all the markup that is required for a fully functional article page in your web application, including all ux-elements, functionality and everything. Then think of how things could be structured using Web Components. How about something like this:

<html>
    <head>

        <link rel="import" href="/html/application/basic.html">
        <link rel="import" href="/html/themes/standard-application.html">
        <link rel="import" href="/html/some/components/someModule.html">

    </head>
    <body>

        <header is="vg-header" theme="vgpluss">
            <vg-menu />
        </header>

        <aside is="vgpluss-sidebar"></aside>

        <article is="vg-article">
            <h1>Example article</h1>
            <p class="summary">
                Blah blah blah
            </p>

            <vg-byline>
                <vg-authors>
                    <vg-author id="3" />
                    <vg-author id="5" />
                </vg-authors>
                <time is="vg-timestamp">2014-08-08 14:16:21</time>
            </vg-byline>

            <section class="body">
                Lorem ipsum blah blah
            </section>

            <vg-comments />
        </article>

    </body>
</html>

Henrik Haugberg | Developer at VG | Structure the world, and make it look good (Frontend and Backend developer). Then disconnect from it all in the Norwegian mountains!


0 comments

    Leave your comment