Extending the Web with Components

by Martin Naumann

The Web is a powerful and ubiquitous platform. It bears all sorts of information, applications, and, most importantly, people.

Why Moving the Web Forward Was (and still is) Hard

This results in a vast amount of browsers and browser versions people use daily.

Everybody expects a modern yet flexible platform for all to use. The variety that makes it a great and open place is what also makes that hard to achieve. There are tests for all relevant platforms, and new features mustn’t break whatever is out there in the world.

Not to mention the standards need time to evolve from a draft to a solid foundation in order to build the future of the Web. Often, the needs and innovation happen so fast that the standards just can’t keep up with developers and users.

The Web is not much more than a box of building blocks – HTML elements, JavaScript APIs, and CSS selectors and attributes.

Everything we build is a combination of those blocks – and it can get us far.

But, as there is a big variety in browsers and versions, we need to make sure that they are not left out – Polyfills and Shims do this.

The downside is that we need a new polyfill for everything new we come up with, and creating new elements tends to get messy.

What are Web Components?

Web components are a set of standards to create your own new building blocks.

There are:

  • Templates: markup for your comments, inactive ’til you summon it.
  • Shadow DOM: hiding all the gory details of the components inner workings.
  • Custom elements: Persuading the browser to show your component when demanded by the markup.
  • Imports: Like importing external style sheets but for whole components.

With these standards you can build elements that are hard to distinguish from native elements.

For instance you could import a component somebody else built into your application like this:

≶x-headlines type="recent" count="5"></x-headlines>

To show a list with the 5 latest article titles in your application. That’s it.

The inner implementation is fully isolated – no CSS rule conflicts, no clashing IDs. The component may allow you to change the style of it or take content from the outside.

Here is a sample:

<x-tabs>
      <section>
        <header>Tab 1</header>
        <article>...</article>
      </section>
      <section>
        <header>Tab 2</header>
        <article>...</article>
      </section>
    </x-tabs>

A Sample Component

Quick note: Currently the sample below only works with a polyfill. Only Chrome 33 with the “Experimental Web Platform features” enabled supports all standards.

So let’s build a weather component, which should provide the following features:

  • The location for the weather should be definable via the city attribute in the HTML markup.
  • There should be a unit’s attribute (either set to “metric” or “imperial”. It should also update the displayed weather information dynamically when the value changes.

Here is the source plus annotations:

<template>
      <style>
        article { text-align: center; }
      </style>
      <article>
        <h1><img /></h1>
        <h2><span class="current"></span>°</h2>
        <p><strong class="min"></strong>° — <strong class="max"></strong>°</p>
      </article>
    </template>
 
    <script>
      var proto = Object.create(HTMLElement.prototype),
          city  = "Zurich",
          units = "metric";
 
      proto.createdCallback = function() {
        var self = this;
        this._root = this.createShadowRoot();
        var tplContent = document.querySelector("template").content;
 
        this._root.appendChild(tplContent.cloneNode(true));
        // This method is public
        this.update = function() {
          var xhr = new XMLHttpRequest();
          xhr.open("GET", "http://api.openweathermap.org/data/2.5/weather?q=" + self.city + "&units=" + self.units, false);
          xhr.onreadystatechange = function() {
            if(this.readyState != 4) return; //We are not ready...
 
            var response = JSON.parse(this.responseText);
 
            //If the API responds with an error, we ignore it
            if(response.message) { return; }
 
            self._root.querySelector("img").src = "http://openweathermap.org/img/w/" + response.weather[0].icon + ".png";
            self._root.querySelector(".current").textContent = response.main.temp;
            self._root.querySelector(".min").textContent = response.main.temp_min;
            self._root.querySelector(".max").textContent = response.main.temp_max;
          }
          xhr.send(null);
        };
      };
 
      proto.attachedCallback = function() {
        this.city  = this.attributes.getNamedItem("city").value || this.city;
        this.units = this.attributes.getNamedItem("units") && this.attributes.getNamedItem("units").value || this.units;
        this.update();
      };
 
      // Let's define the "city" property. Whenever it changes, we save that value and call update()
      Object.defineProperty(proto, "city", {get: function() { return city; }, set: function(newVal) {
        city = newVal;
        this.update();
      }});
 
      Object.defineProperty(proto, "units", {get: function() { return units; }, set: function(newVal) {
        units = newVal;
        this.update();
      }});
 
      var FilterList = document.registerElement("x-weather", { prototype: proto });
    </script>
 
    <x-weather city="Zurich"></x-weather>

Popping the Question: When Can We Use It?

More or less today, but (there’s always a catch, isn’t there?) the drafts of all the standards mentioned herein are pretty early in their development.

As of now, there are two libraries for web components available:

You can either use those libraries or take the Polymer polyfills to use the standard interfaces.

To use the polyfills, just include its platform.js.
The polyfill supports all “evergreen” browser versions of the major browsers.

Component-O-Rama

Thanks to the Polymer polyfill, we can consider web components a usable way to build web applications.

Browsers are about to provide implementations in their latest versions – so watch that space!

As a bonus, you can use a lot of ready-to-use components from others.

There are collections like Brick or the Polymer elements waiting for you.

The App Maker is a great way to try it without writing any code. Your results work on phones and tablets, too.

I look forward to more semantic markup and reusable components.

For more complete information about compiler optimizations, see our Optimization Notice.