4.2 Web Components

In notebook:
edX Advanced HTML5
Created at:
2016-01-19
Updated:
2016-01-19
Tags:
libraries React JavaScript

Web components enable you to use custom HTML elements in your HTML documents, that will render as complex widgets: a better looking calendar, an input text with vocal recognition, a nice chart, etc

They're encapsulated as single html elements: <x-gif src="http://i.imgur.com/iKXH4E2.gif" ping-pong></x-gif>

To include an element, use the <link> element: <link rel="import" href="dist/x-gif.html">

Resources: webcomponents.org customelements.io polyfill

The four specifiacations that make up Web components:

  • HTML templates
  • Shadow DOM
  • Custom Elements
  • HTML Imports

HTML templates

You create an html ​template​ element that later you clone to add it to the DOM and display it on the page:

<template id="mytemplate">
   <img src="" alt="great image">
   <div class="comment"></div>
</template>

You normally add this template element on the top of the page, then clone it into your page with document.importNode(templateContent, true) The parameter true means it will be a deep copy of the content.

Simple example to clone:

// 1. Get a reference to you template element
var t = document.querySelector('#mytemplate');

// 2. Update it's contents (e.g. add a logo)
// Populate the src at runtime.
t.content.querySelector('img').src = 'http://webcomponents.github.io/img/logo.svg';

// 3. import the element (it will be a copy) to the document
// Clone the template, sort of "instantiation"!
var clone = document.importNode(t.content, true);
document.body.appendChild(clone);

The Shadow DOM

Provides encapsulation: to the parent document the Shadow DOM is just one element all the internals are hidden.

Shadow root

It's a kind of node

Shadow host

An element that has a shadow root associated with it is called a shadow host. It's the element that will contain the shadow root.

It's the shadow* host* that is rendered and not the shadow* root.*

How to use

  1. get a reference to a standard HTML DOM element (the host)
  2. run createShadowRoot() on it (the root)
  3. the original content (e.g. textContent) is totally removed, new content has to be programatically added
  <button>Hello, world (not rendered)!</button>
<script>
   var host = document.querySelector('button');
   var root = host.createShadowRoot();
   root.textContent = 'the shadow root node is rendered';
</script>

Combining HTML templates with the shadow DOM

  1. create an HTML template element with html, css, js in it (​<template>...</template>
  2. select an HTML element in the document to declare it as root (​var root = document.querySelector("mydiv").createShadowRoot()​)
  3. copy insert your template into root​root.appendChild(document.importNode(t.content, true)))
Notice that the content of the original template remains unchanged, unlike when you convert an existing DOM element to a shadow root.
The css, js inside the template element will not effect other elements in the page.

Insert the shadow host content into the cloned template

The ​<template>​ element provides a ​<content>​ element. If you add this element to the template, then all the contents (including html elements) of the host element will be copied into it (the content element). The above syntax remains the same, you just need to add the ​<content>​ child element to your ​<template>​.

​select​ attribute

To choose the element to add when using multiple ​<content/>​ blocks.
You can add a ​select="queryselector"​ to the ​<content>​ element the will work as a selector inside the host element to select which child node to insert. 

External styling of the Shadow DOM

Insert CSS styles with JavaScript

​root.innerHTML = '<style>h3{ color: red; }</style>' + //your content here

CSS :HOST selector to style the host from the shadow root

Inside your shadow DOM element:

root.innerHTML = root.innerHTML = '\<style\>' +

 ':host { text-transform: uppercase; background-color:red;}' +

 '\</style\>' +

 '\<content\>\</content\>';

use :host(:hover) to react to mouse events The :host-context(<selector>) pseudo class matches the host element if it or any of its ancestors matches the <selector>.

Style the shadow DOM from the outside

Use ::shadow selector (.active::shadow a{...})

HTML Custom Elements

Create new HTML elements (e.g. ​<my-widget>​)
Rules:
  • The element's name must have a dash
  • The prototype must start from an already existing HTML element (from the spec) or another custom element

The blueprint for defining a custom element. 

1. Define the custom element and contents to be injected in html

  <body>
   <my-widget>
     <span id="titleToInject">Title injected</span>
     <span id="partToInject">Paragraph injected</span>
   </my-widget>
</body>

2. Create an HTML template with ​content​ elements and ​select​ attributes

  <template id="mytemplate">
 <style>
   h1 {
     color:white;
     background:red;
   }
 </style>
 <h1 part='heading'>
     <content select="#titleToInject"></content>
 </h1>
 <p part="paragraph">
    <content select="#partToInject">
 </content>
</p>
</template>

3. Do the JavaScript ceremony

  1. copy import your template element ​var clone = document.importNode(document.querySelector("#mytemplate").content, true)
  2. create a new object from ​HTML.prototype​ : ​var widgetProto = Object.create(HTMLElement.prototype)
  3. declare it as a shadow root when it's created: ​widgetProto.createdCallback = () => this.createShadowRoot().appendChild(clone)
  4. register the new element with ​document.registerElement​ : ​var Widget = document.registerElement("my-widget", {prototype: widgetPrtoto});

HTML Imports

Just add a <link rel="imports" href="your_html_file"> in order to import all the html/css/js that define the Web components you plan to use