Web Components Introduction
Web Components is a standard that is getting really popular these days, Browser support is getting better and we can use libraries such as Polymer, XTags and Bosonic. In addition, Angular 2 will introduce support for Web Components, Shadow DOM, Templating, etc. It’s important to understand all these concepts in order to adopt faster new frameworks and libraries.
In a nutshell, Web Components allow us to encapsulate and share reusable widgets. The main idea is to been able to bundle markup and styles to create custom components, all the code will be hidden from the outside world, the markup and the styles will be isolated.
In this tutorial we will create our first Web Component, a simple panel that will allow us to define the title and the content. When clicking the collapse/expand button the content of the panel will collapse or expand accordingly. It’s recommended to test all the code in this tutorial on Chrome 31+ (Enable “Experimental Web Platform features” in about:flags) or Firefox 23+.
When we are done with this component, we will be able to use it like in the following code snippet.
If we place that code in an empty html file, we will see that nothing special will happen, that’s because the browser doesn’t recognize the tag my-panel. In order to make this work, we need to register that component as shown below.
In the first step we are creating the prototype for our Web Component, we are extending from the HTMLElement prototype, but we can extend from any other such as HTMLInputElement, HTMLVideoElement, basically any component that already exist.
In the second step we are registering our new component, the first parameter is the name of the component, in this case my-panel. It’s important to define the name of the new component with at least one dash, otherwise there will be an error like this.
Uncaught SyntaxError: Failed to execute ‘registerElement’ on ‘Document’: Registration failed for type ‘mypanel’. The type name is invalid.
As a second parameter we define an object of configurations, for now there’s only the prototype configuration.
Now we have our new component, the new tag is been recognized by the browser and we have our first web component ready for use! However, this component is not doing anything at the moment, is just a container like any other div, the next thing we need to do is define a template to completely change the look and feel of our component.
A template will allow us to define markup for reuse, all the content of the template will not be rendered into the DOM when declared.
We are going to use a template to define the markup of our custom panel, this feature is supported by all major browsers, all we need to to do is define a chunk of markup inside of the template tag.
Here we are defining the header and the body of a panel. Inside of the header we have a collapse/expand button, we could have whatever we want but for this example we will keep things simple. Next thing we need to do is apply the styles to the panel. We will define the styles inside of this template, otherwise the styles will not get applied, at this time browsers don’t allow to include stylesheets inside a template.
There’s not much to explain in the previous snipped of code, just simple CSS. Now we need to apply the template to our component, but before that we need to know a few important concepts.
Allow us to hide all the markup and styles from the outside world. We can have a very complex component and only expose just a few nodes to the outside. In this case we will have a custom panel with buttons, header and body, but we will only expose one single node, everything else will be hidden.
Creating a shadow DOM is as simple as in the following code.
The createShadowRoot method creates a document fragment that we can fill with anything we need, in this sample we are appending some text. In the previous code snippet, we are creating the fragment directly in the body of the page, however we can use any other element. If you open the browser’s console and inspect the DOM, you will see our new element.
Everything inside the #shadow-dom fragment will not be accessed by other, it is just like having an iframe in our document.
The shadow host
The element is used to call the createShadowRoot method, is called the shadow host, this is the only element that can be accessed by another, in the previous code we used the body, but it can be anything else. For example, when we are defining an audio, the audio tag will be the host and the sources will be the content.
In our custom component the shadow host will be element, which means we need to call the createShadowRoot from that node.
The shadow root
The shadow root is the document fragments that gets created when calling the createShadowRoot method, everything inside of this fragment will be rendered to the document but it can’t be accessed or modified.
Every HTML and CSS inside of the shadow root will be protected by an invisible barrier, that is called the shadow boundary. This boundary blocks any access by styles or scripts to the content of the shadow root.
This means that if we define styles for our component using a template, the styles can not be changed from the styles in the document. Scripts are not able to read, add or remove other nodes and everything is completely hidden.
Applying the template
Now that we know the important concepts, let’s continue building our web component. We need to apply our template as follows.
In the first step we are getting the template from the document, the selector is very simple because we only have one template in the document. In the second step we are creating the createdCallack method. This method get executed automatically when the parser finds our component in the document. In here we can initialize our web component. In the third step we are creating the shadow root, in this case the shadow host will be our custom tag . In the last step we are appending to the shadow root the content of the template. This will do the magic and our template will be applied to the component, all images in our template will be downloaded, scripts will be executed and the styles applied.
Our current web component doesn’t do anything yet, for this example we will add a listener to the little arrow at the top-right to allow the body of the panel collapse and expand.
In the first step we search the node that we need to attach the listener, in this case is a span with the class collapse.
In the second step we add the click event listener to the span node.
The content of the listener is very simple, basically we are changing the display property of the body. This will hide or show the body when the user clicks the arrow at the top.
Our current component is simple, but from here we can start adding a lot more functionality and features, such as resizing, maximizing, minimizing, etc. All that code will be encapsulated in the Web Component. The beauty of this is that we will be able to share our component without the worry of conflicts with third party libraries or even our other components.
Here is the full source code: https://github.com/crysfel/IntroToWebComponents/blob/master/index.html