HTML5 Web Components

Michel Buffa
April 2014

buffa@unice.fr

Web Components: create your HTML5 elements!

<multitrack src="Bass">
<tr>
    <td class="trackBox" style="height : 75px">
        <progress class="pisteProgress" id="progress0" value="3"
                  max="3" style="width : 75px">
        </progress>
        Bass
        <div style="float : right;">
            <button class="mute" id="mute0" onclick="muteUnmuteTrack(0);"
                    disabled="disabled">
                <span class="glyphicon glyphicon-volume-up"></span>
            </button>
            <button class="solo" id="solo0" onclick="soloNosoloTrack(0);"
                    disabled="disabled">
                <img src="../img/earphones.png">
            </button>
        </div>
        <span id="volspan">
            <input type="range" class="volumeSlider" id="volume0" min="0"
                   max="100" value="100"
                   onchange="setVolumeOfTrackDependingOnSliderValue(0);">
        </span>
    </td>
</tr>
                

Web Components : use the same tools browser's developers use!

How do you think they made this <video> element?


Web Components rely on several W3C APIs

Templates

They are parts of HTML code (including scripts and styles) that can be reused (similar to mustache for example)

These parts of code are inert (CSS will not be applied, JS not executed, images not loaded, videos not played, etc) until the template is used.

 <template id="mytemplate">
    <img src="" alt="great image">
    <div class="comment"></div>
</template>               
It's ok to have src empty here... we'll set it later...

To use a template, clone its content!

var t = document.querySelector('#mytemplate');
// Populate the src at runtime.
t.content.querySelector('img').src = 'http://webcomponents.github.io/img/logo.svg';

var clone = document.importNode(t.content, true);
document.body.appendChild(clone);

Shadow DOM

Provides DOM encapsulation: hide what is not necessary!

This is already used by browsers' developers for <audio> or <video> elements etc.

The three rules of Shadow DOM:

  1. With Shadow DOM, elements can get a new kind of node associated with them: a shadow root.
  2. An element that has a shadow root associated with it is called a shadow host.
  3. The content of a shadow host isn’t rendered; the content of the shadow root is rendered instead.

Shadow DOM: small demonstration

<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>
                
Demonstration!

Shadow DOM: encapsulate styles or scripts...

Let's mix templates and shadow DOM! Demonstration!

<template id="mytemplate">
  <style>
    h1 {color:white; background:red}
    </style>
  <h1>This is a shadowed H1</h1>
</template>
// Instanciate the template
var t = document.querySelector('#mytemplate');
var clone = document.importNode(t.content, true);

// Create a root node under our H1 title
var host = document.querySelector('h1');
var root =  host.createShadowRoot();

// Put template content in the root node
root.appendChild(document.importNode(t.content, true));
                

Shadow DOM: authorize or forbid external CSS rules, or reset inheritance


Can be affected by external CSS: (Demonstration!)
root.applyAuthorStyles = true; // false by default
CSS reset your internal styles: (Demonstration!)
root.resetStyleInheritance = true; // false by default
                

Styling parts

<template id="mytemplate">
    <h1 part="heading">This is a shadowed H1</h1>
    <p part="paragraph">Paragraph part</p>
</template>

<body>
    <div id="myWidget"></div>
</body>
#myWidget::part(heading) {
  color:red;
}
                

Insert content from the host element inside the shadow DOM


Demonstration!
 <template id="mytemplate">
  <h1 part='heading'>This is a shadowed H1</h1>
  <p part="paragraph">
    <content></content>
  </p>
</template>

<body>
  <H1 class="myWidget">Injected content</h1>
</body>
 

Shadow DOM: more specific content injection using the select attribute

this attribute's value is a CSS selector: Demonstration!

 <template id="mytemplate">
    <h1 part='heading'><content select="#titleToInject"></content></h1>
    <p part="paragraph">
    <content select="#partToInject"></content>
</p>
</template>

<body>
    <div class="myWidget">
        <span id="titleToInject">Title injected</span>
        <span id="partToInject">Paragraph injected</span>
    </div>
</body>
            

Useful tool: shadow DOM visualizer by E.Bilderman

Custom Elements

They are elements that extends the browser (the browser will render them in addition to HTML5 ones).

Basic usage:

  document.registerElement('tag-name', {
    prototype: proto
})
                 

The element's new name should have a dash!

The prototype must inherit from HTMLElement.

Example

 // Instanciate the template
var t = document.querySelector('#mytemplate');
var clone = document.importNode(t.content, true);

var widgetProto = Object.create(HTMLElement.prototype);

widgetProto.createdCallback = function() {
  car root = this.createShadowRoot();
  root.appendChild(clone);
}

var Widget = document.registerElement("my-widget", {
  prototype : widgetProto
})
 

Usage and demonstration

The new element can be used as any HTML5 element: Demonstration!

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

Packaging your components: HTML imports!

Similar to including CSS in your page!

Package your components in a HTML page (that can use CSS, JS, etc) and import it!

It cannot be simpler than:

<head>
<link rel="imports" href="components/myComponents.html">
</head>
<body>
  <my-widget>
     <span id="titleToInject">Title injected</span>
    <span id="partToInject">Paragraph injected</span>
  </my-widget>
</body>