WebComponents are the salvation of Component based web development. When all front-end frameworks are pushing for Component approach & thinking, WebComponents are native solution to achieve same.

WebComponents are the salvation of Component based web development.

Where all the front-end frameworks are pushing for Component approach and thinking in component style, DOM has the native way to address this. WebComponents is the collective solution to have components in the Browser natively. This collective solution includes:

To get up and running with WebComponents, you only need the CustomElements V1 polyfill which provides a generic way to create components and lifecycle methods, which you can obtain from following repository:

Many would say that you will need shadowDOM , template tags, HTML imports for your custom elements. They are right but not completely. You can create your components without them as well.

CustomElements

CustomElements are the elements similar to native HTML elements like div , span etc. These are the extension of HTMLElement constructor and other similar constructors based on the type of CustomElement you wanna create.

Let's see an example; consider you wanna create a web component which will serve as a quick creation of figure with img and figcaption together. Normally the HTML will look like following:

<figure> <img src="https://developer.cdn.mozilla.net/media/img/mdn-logo-sm.png" alt="An awesome picture"> <figcaption>MDN Logo</figcaption> </figure>

Example taken from https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figure

And the component will look like:

<img-figure caption="MDN Logo" alt="An awesome picture" src="https://developer.cdn.mozilla.net/media/img/mdn-logo-sm.png" ></img-figure>

The basic component code will be as follows:

class ImgFigure extends HTMLElement { connectedCallback() { this.src = this.getAttribute("src") || null; this.caption = this.getAttribute("caption") || ""; this.alt = this.getAttribute("alt") || null; this.render(); } render() { this.innerHTML = this.template({ src: this.src, alt: this.alt, caption: this.caption }); } template(state) { return ` <figure> <img src="${state.src}" alt="${state.alt || state.caption}"> <figcaption>${state.caption}</figcaption> </figure> `; } } customElements.define('img-figure', ImgFigure);

And its usage through JavaScript will be as follows:

// create element const i = document.createElement('img-figure'); //set the required attributes i.setAttribute('src', '//res.cloudinary.com/time2hack/image/upload/goodbye-xmlhttprequest-ajax-with-fetch-api-demo.png'); i.setAttribute('caption', 'GoodBye XMLHttpRequest; AJAX with fetch API (with Demo)'); i.setAttribute('alt', 'GoodBye XMLHttpRequest'); //attach to the DOM document.body.insertBefore(i, document.body.firstElementChild);

Or Create the element right in DOM like as follows:

<img-figure style="max-width: 400px" src="//res.cloudinary.com/time2hack/image/upload/ways-to-host-single-page-application-spa-static-site-for-free.png" alt="Free Static Hosting" caption="Ways to host single page application (SPA) and Static Site for FREE"> </img-figure>

Demo:

Lets take a look at the component creation in detail:

Initial Required part

All custom elements/components extend the basic HTMLElement object and have the features of it like the attributes, styles etc.

class ImgFigure extends HTMLElement { connectedCallback() { // .... } }

And the connectedCallback is executed when they are attached to the DOM. So we place the initial code in this function.

Final Required Part

Finally we need to register the element to the DOM, so that when DOM sees that element, it will instantiate the above mentioned Class rather than HTMLElement .

customElements.define('img-figure', ImgFigure);

And that's it. These parts will register the component and available to be created through document.createElement API.

Play with WebComponents (another Demo):

But what if you wanna use the make it react to any attribute change?

For that, there are two pieces of code that should be present on the Component's class.

One: Need to register the observable attributes:

static get observedAttributes() { return ['attr1', 'attr2']; }

And Second: Need to react on the observable attributes' changes:

attributeChangedCallback(attr, oldValue, newValue) { if(oldValue === newValue){ return; } if (attr == 'attr1') { // some stuff } if (attr == 'attr2') { // some other stuff } }

Let's see these two pieces of code in our old img-frame Component:

class ImgFigure extends HTMLElement { connectedCallback() { this.src = this.getAttribute('src') || null; this.caption = this.getAttribute('caption') || ''; this.alt = this.getAttribute('alt') || null; this.render(); } static get observedAttributes() { return ['src']; } attributeChangedCallback(attr, oldValue, newValue) { if(oldValue === newValue){ return; } if (attr === 'src') { this.querySelector('img').src = newValue; } } render() { this.innerHTML = template({ src: this.src, alt: this.alt, caption: this.caption, }); } }

This way you can create you custom elements without needing to worry about much of the browser support.

The lifecycle methods of the customElement are:

Method Usage/Description constructor() Called when the element is created or upgraded connectedCallback() Called when the element is inserted into a document, including into a shadow tree disconnectedCallback() Called when the element is removed from a document attributeChangedCallback(attrName, oldVal, newVal, namespace) Called when an attribute is changed, appended, removed, or replaced on the element (Only called for observed attributes) adoptedCallback(oldDocument, newDocument) Called when the element is adopted into a new document

Support?

Can I Use custom-elementsv1? Data on support for the custom-elementsv1 feature across the major browsers from caniuse.com.

But wait! Firefox is right there to support customElements :

Summary:

This is basically an after the fact notification that we're in progress of

implementing Custom Elements (both autonomous custom elements and

customized built-in elements) and the implementation for old spec, which

was never exposed to the web, will be removed. We are close to finishing

the implementation, but there are still some performance works before

shipping the feature. We plan to enable it only on Nightly first to get

more feedback from users. (there will be a intent-to-ship before shipping

the feature)

https://groups.google.com/forum/#!msg/mozilla.dev.platform/BI3I0U7TDw0/6-W39tXpBAAJ

Detailed Reading on CustomElements: https://developers.google.com/web/fundamentals/web-components/customelements

But What about ShadowDOM?

ShadowDOM

ShadowDOM is a way to encapsulate the underlying DOM and CSS in a Web Component. So if you really need the encapsulation; cases when you are providing widgets to third party; use ShadowDOM.

Primarily you can attach ShadowDOM with attachShadow and then perform operations on it:

element.attachShadow({mode: 'open'});

Let's see an example of ShadowDOM:

The attachShadow method needs a configuration object which says only about the encapsulation. The object will have key mode which will have value either open or closed .

And as explained at https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow:

mode : A string specifying the encapsulation mode for the shadow DOM tree. One of:

element.shadowRoot === shadowroot; // returns true

closed : Specifies closed encapsulation mode. This mode denies any access to node(s) of a closed shadow root from an outside world

element.shadowRoot === shadowroot; // returns false element.shadowRoot === null; // returns true

The attachShadow returns the ShadowRoot which you can use as a regular document and perform operations on it.

Support?

Can I Use shadowdomv1? Data on support for the shadowdomv1 feature across the major browsers from caniuse.com.

More/Detailed reading on ShadowDOM: https://developers.google.com/web/fundamentals/web-components/shadowdom

HTML Template

The HTML templates provide the mechanism to send the markup on the page but not being rendered. This is a huge help if you wanna keep your JavaScript bundle size to minimal.

Once the template is on the document, it can be cloned and then filled with the relevant dynamic content with JavaScript

It's support is still not wide enough; so you can check that with following code

if ('content' in document.createElement('template')) { // operate on the temmplate }

Considering that the browser being used supports the template tags; you can use them in following way:

<template id="img-figure"> <figure> <img /> <figcaption></figcaption> </figure> </template>

let template = () => `Template tag not supported`; const t = document.querySelector('#img-figure'); if ('content' in document.createElement('template')) { template = (state) => { const img = t.content.querySelector('img'); const caption = t.content.querySelector('figcaption'); img.setAttribute('src', state.src); img.setAttribute('alt', state.alt || state.caption); caption.innerHTML = state.caption; return document.importNode(t.content, true); } } else { template = (state) => { //fallback case const d = document.createElement('div'); d.innerHTML = t.innerHTML; const img = d.querySelector('img'); const caption = d.querySelector('figcaption'); img.setAttribute('src', state.src); img.setAttribute('alt', state.alt || state.caption); caption.innerHTML = state.caption; return d.firstElementChild; } } class ImgFigure extends HTMLElement { connectedCallback() { this.src = this.getAttribute("src") || null; this.caption = this.getAttribute("caption") || ""; this.alt = this.getAttribute("alt") || null; this.render(); } render() { this.appendChild(template({ src: this.src, alt: this.alt, caption: this.caption, })); } } customElements.define('img-figure', ImgFigure);

Read more on HTML Template here: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template

HTML Imports

The HTML Imports are the easiest way to deliver the WebComponents to the desired location.

These work in same way as you import external stylesheets in your document.

<link rel="import" href="img-figure.html" />

And then your component file img-figure.html can have other dependency added, like as follows:

<link rel="stylesheet" href="bootstrap.css"> <script src="jquery.js"></script> <script src="bootstrap.js"></script> ...

https://www.html5rocks.com/en/tutorials/webcomponents/imports/

Help

Following places will be able to help you more in understanding the concepts of WebComponents:

People/Companies using WebComponents

To motivate you about WebComponents:

Just released our new Menu Board software to every McDonald's store in the US. Built with @polymer to be completely modular. pic.twitter.com/cLHcNaWmok — Andrew Stacy (@AJStacy06) April 27, 2017

Yay! @github is using web components. All of the timestamps are <local-time> custom elements. pic.twitter.com/E10Ljl1elf — Eric Bidelman (@ebidel) May 7, 2014

Others who are not very social 😉

https://github.com/Polymer/polymer/wiki/Who's-using-Polymer?

Final Thoughts

WebComponents are great. And the slowly all browsers are moving towards complete support.

You can use them with regular JavaScript script include as well if you are not sure about the support for HTML imports and template tags.

Special Thanks

Thanks a lot Alex and Nico for helping and reviewing this post:

Let us know what you think about the WebComponents via comments.

If you are stuck somewhere while implementing WebComponents, reach out through comments below and we will try to help.