Building Custom Web Components with X-Tag

Pankaj Parashar
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.

Frequently Asked Questions (FAQs) about Building Custom Web Components with X-Tag

What is X-Tag and why is it important in web development?

X-Tag is a small JavaScript library that simplifies the creation of custom, reusable HTML elements. It’s a part of the Web Components suite of technologies, which also includes Shadow DOM, HTML Templates, and Custom Elements. X-Tag is important in web development because it allows developers to create their own HTML elements, encapsulate their code, and keep their codebase clean and maintainable. It also promotes code reuse, which can significantly speed up the development process.

How does X-Tag differ from other web component libraries?

X-Tag is unique in its simplicity and ease of use. Unlike other libraries, X-Tag does not require any build steps or transpilation, and it has a very small footprint. It also has a simple, intuitive API that makes it easy to define custom elements and their behavior. Furthermore, X-Tag is compatible with all modern browsers and can be used alongside other libraries and frameworks.

Can I use X-Tag with other JavaScript libraries or frameworks?

Yes, X-Tag is designed to be used with any JavaScript library or framework. It does not impose any restrictions or specific architectural patterns, so you can easily integrate it into your existing projects. Whether you’re using jQuery, React, Angular, or Vue.js, you can use X-Tag to create custom elements that can be reused across your application.

How do I define a custom element with X-Tag?

Defining a custom element with X-Tag is straightforward. You simply call the xtag.register function, passing in the name of your custom element and an object that defines its behavior. This object can include lifecycle methods, accessors, and event handlers. Here’s a basic example:

xtag.register('my-element', {
lifecycle: {
created: function() {
this.textContent = 'Hello, world!';
}
}
});

What are lifecycle methods in X-Tag?

Lifecycle methods are special methods that get called at different stages of a custom element’s life. X-Tag supports four lifecycle methods: created, inserted, removed, and attributeChanged. These methods allow you to perform specific actions when an element is created, added to the DOM, removed from the DOM, or when one of its attributes changes.

How do I handle events with X-Tag?

X-Tag provides a simple way to handle events on your custom elements. You can define event handlers in the events object when registering your element. For example, to handle a click event, you would do something like this:

xtag.register('my-element', {
events: {
click: function(e) {
console.log('Element clicked!');
}
}
});

Can I use X-Tag to create shadow DOM elements?

Yes, X-Tag supports the creation of shadow DOM elements. This allows you to encapsulate your element’s styles and markup, keeping them separate from the rest of your document. To create a shadow root, you can use the this.createShadowRoot() method inside your element’s created lifecycle method.

How do I style my custom elements?

You can style your custom elements just like any other HTML elements, using CSS. If your element uses shadow DOM, you can include a <style> tag inside your shadow root to define styles that only apply to your element. You can also use CSS custom properties (variables) to make your styles more flexible and reusable.

Can I use X-Tag to create custom form elements?

Yes, X-Tag can be used to create custom form elements. However, keep in mind that custom form elements do not participate in form submission or constraint validation by default. You will need to provide your own logic for these features.

Is X-Tag still maintained and supported?

As of the time of writing, X-Tag is not actively maintained. The last release was in 2017. However, the library is stable and can still be used in projects. If you encounter any issues or need new features, you may need to implement them yourself or consider using a different library.