Building Custom Web Components with X-Tag

Pankaj Parashar
Share

After Google and Mozilla‘s solutions for web components, it is now Microsoft’s turn to enter this space with their public release of the X-Tag library. The official website defines it as follows:

X-Tag is a Microsoft supported, open source, JavaScript library that wraps the W3C standard Web Components family of APIs to provide a compact, feature-rich interface for rapid component development. While X-Tag offers feature hooks for all Web Component APIs (Custom Elements, Shadow DOM, Templates, and HTML Imports), it only requires Custom Element support to operate. In the absence of native Custom Element support, X-Tag uses a set of polyfills shared with Google’s Polymer framework.

In other words, X-Tag is to Microsoft what Polymer is to Google. The only thing common to both of them is the underlying polyfill that enables support for web components in non-supporting browsers.

How is X-Tag Different than Polymer?

Polymer combines all four web component technologies, namely, HTML Imports, Custom Elements, Shadow DOM and HTML Templates into a single package. It provides the developer with an easy-to-use API for building custom web components. On the other hand, X-Tag only provides the Custom Element API to build custom web components. Of course, you could also still use other web component APIs in conjunction with the X-Tag library.

Isn’t X-Tag, a Mozilla Project?

Yes it was, but it now no longer is. After some digging, I managed to find out that the original developer of the X-Tag project, Daniel Buchner, used to work at Mozilla when he created the library. But since then, he has moved to Microsoft and continued the project. Moreover, he was the sole contributor to the library with help from an ex-Mozillian. Hence, it is now a Microsoft-backed project founded by an ex-Mozillian.

Getting Started with X-Tag

In this article, we will build a custom element using the X-Tag library to embed a Google map street view using the following markup:

<google-map latitude="40.7553231" longitude="-73.9752458"></google-map>

The <google-map> custom element will have two attributes, latitude and longitude, to specify the coordinates of a location. Since these attributes are optional, we will also assign default values for each one of them in case the developer fails to define them in the markup.

Let’s start by including the X-Tag JavaScript library in the <head> of our document.

<!DOCTYPE html>
<html>
  <head>
    <!-- X-Tag library including the polyfill -->
    <script src="https://rawgit.com/x-tag/core/master/dist/x-tag-core.min.js"></script>
  </head>
  <body>
    <!-- Custom element markup will appear here -->
    
    <script>
      <!-- The behavior of the custom element will be defined here -->
    </script>
  </body>
</html>

It is important to include the X-Tag library in the <head> of the document. This is so that it is downloaded and parsed completely before the rendering engine encounters the custom element markup that we will use inside the <body>.

Defining the Custom Element

Unlike Polymer, the process of creating a custom element with X-Tag is completely JavaScript-driven. The X-Tag library provides a bunch of useful methods, callbacks, and properties to define custom behavior for our element. A typical skeleton for creating a custom element with X-Tag looks like the following:

xtag.register('google-map', {

  content: '',

  lifecycle: {
    created  : function(){ 
      /* Called when the custom element is created */ 
    },
    inserted : function(){ 
      /* Called when the custom element is inserted into the DOM */ 
    },
    removed  : function(){ 
      /* Called when the custom element is removed from the DOM */ 
    },
    attributeChanged: function(attrName, oldValue, newValue){
      /* Called when the attribute of the custom element is changed */
    }
  },

  accessors : {},
  methods   : {},
  events    : {}
});
  • register() – This is the most important method in the library. It accepts the name of the custom element as the first argument followed by the object definition. As the name suggests, this is responsible for registering the custom element in the DOM.
  • content – Quite often, the custom element might need some additional markup for structure or presentation. This accepts an HTML string or a multiline comment string to inject the markup into the DOM.
  • lifecycle – This contains useful callback methods, designed to target different stages in the lifecycle of the custom element.
  • accessors – This provides a common interface to access object attributes, setters, and getters, and link them with the corresponding HTML attributes. When attributes are linked to a getter/setter, their states always remain in sync.
  • methods – The custom element will likely need some of their own, unique methods to provide the desired functionality. Just add a methods object to the top-level xtag.register() definition object and include all the user-defined methods within it.
  • events – This is responsible for attaching events to the custom element. The keys of this object are the names of the events that you wish to attach to the custom element like tap, focus etc.

The basic idea is to embed the google map using an iframe and specify the src url in the following format:

https://www.google.com/maps/embed/v1/streetview?key=<API_KEY>&location=<LATITUDE>,<LONGITUDE>

To get the API_KEY, follow the steps described here. Once you have the API_KEY, we will create an iframe inside the created callback method of the lifecycle object and specify the src property in the above format.

// Insert your API key here
var API_KEY = <API_KEY>;

xtag.register('google-map', {
  lifecycle: {
    created: function() { 
      var iframe = document.createElement('iframe');
      iframe.width = 600;
      iframe.height = 600;
      iframe.frameBorder = 0;
      iframe.src = 'https://www.google.com/maps/embed/v1/streetview?key=' + 
                    API_KEY + '&location=40.7553231,35.3434';
      this.appendChild(iframe);
    }
  }
});

The above code works but we need to get rid of the hard coded coordinate values in the iframe.src and instead source the values directly from the custom element attributes. To do this, we will make use of the accessors object, with the attribute names forming the keys. We link them with the HTML attributes by declaring attributes: {}.

accessors: {
  latitude: {
    attribute: {},
    set: function(value) {
      this.xtag.data.latitude = value;
    },
    get: function() {
      return this.getAttribute('latitude') || "40.7553231"; // Default value
    }
  },
  longitude: {
    attribute: {},
    set: function(value) {
      this.xtag.data.longitude = value;
    },            
    get: function() {
      return this.getAttribute('longitude') || "-73.9752458"; // Default value
    }
  }
}

We can then use these variables directly while specifying the src url:

iframe.src = 'https://www.google.com/maps/embed/v1/streetview?key=' + 
             API_KEY+'&location=' + 
             this.latitude+',' + 
             this.longitude;

Applying Finishing Touches

Styling a custom element is similar to styling any other HTML tag. Classes, IDs, and attribute selectors work as expected with custom elements. For instance, we’ll add a box-shadow and border-radius to our newly created <google-map> custom element.

google-map { 
  display: inline-block;
  box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 
              0 1px 5px 0 rgba(0, 0, 0, 0.12), 
              0 3px 1px -2px rgba(0, 0, 0, 0.2);
  padding: 1em;
  border-radius: .25em;
}

Using the custom element is now as simple as including the following markup in the <body>:

<!-- Use the custom element -->
<google-map latitude="40.7553231" longitude="-73.9752458"></google-map>

The end result is visible in the CodePen below:

See the Pen X-Tag custom element by SitePoint (@SitePoint) on CodePen.

Although browser support for web components is a bit sketchy, our demo using the X-Tag library with the polyfill should work on all modern browsers including IE9 and above.

Final Thoughts

In comparison to Polymer, X-Tag provides a much simpler API to understand and implement custom elements, mostly due to the lack of complex features that Polymer has. If you intend to keep your setup light and flexible, and you don’t have a good use case for the other web component APIs, then X-Tag certainly appears to be the right candidate for the job.