Mike North
CTO Levanto Financial
The Road to Native Web Components
INTRODUCTION
WHO’S THIS GUY?
▸ CTO Levanto Financial
▸ frontendmasters.com instructor
▸ Product Guy
▸ I do a lot of JavaScript OSS stuff
▸ I work with Ember & React a lot
DOCUMENT VIEWER
DOCUMENT
INTRO
DO YOU USE WEB COMPONENTS? (YES)
INTRO
THE W3C WEB COMPONENT SPEC
HTML IMPORTS
HTML TEMPLATES
CUSTOM ELEMENTS
SHADOW DOM
INTRO
THE W3C WEB COMPONENT SPEC
HTML IMPORTS
▸ require() for HTML
▸ onload, onerror events
▸ can bring in css/html/js
▸ closure-like js characteristics
INTRO
THE W3C WEB COMPONENT SPEC
CUSTOM ELEMENTS
▸ Custom HTML vocab
▸ Life-cycle
▸ Extensibility
▸ Interoperability (we hope?)
INTRO
THE W3C WEB COMPONENT SPEC
HTML TEMPLATES
▸ Inert HTML in the DOM
▸ Defer loading / rendering
▸ Think: handlebars/jade/etc…
INTRO
THE W3C WEB COMPONENT SPEC
SHADOW DOM
▸ CSS Encapsulation
▸ DOM encapsulation
▸ Each component has own
subtree w/in parent page
INTRO
THE W3C WEB COMPONENT SPEC
HTML IMPORTS
HTML TEMPLATES
CUSTOM ELEMENTS
SHADOW DOM
HTML TEMPLATES
<!DOCTYPE html>
<html>
<head></head>
<body>
<template id="something">
<div class="frame">
<h3 class="title">I am a title</h3>
<p class="body">I am the body</p>
</div>
</template>
</body>
</html>
<template id="something">
<div class="frame">
<h3 class="title">I am a title</h3>
<p class="body">I am the body</p>
</div>
</template>
<template id="something">
<!-- Some style -->
<style type="text/css">
.title {
font-size: 24px;
}
</style>
<!-- Some behavior -->
<script type="text/javascript">
</script>
<!-- Some structure and content -->
<div class="frame">
<h3 class="title">I am a title</h3>
<p class="body">I am the body</p>
</div>
</template>
// Grab the template
var tmpl = document.querySelector(‘#something');
Add the cloned document fragment to the DOM
document.body.appendChild(
// Clone the template's document fragment
document.importNode(tmpl.content, true)
);
// Grab the template
var tmpl = document.querySelector(‘#something');
Add the cloned document fragment to the DOM
document.body.appendChild(
// Clone the template's document fragment
document.importNode(tmpl.content, true)
);
http://codepen.io/TrueNorth/pen/xbyVgL?editors=101
HOW DO FRAMEWORKS
HANDLE THIS?
define('examples/templates/index', ['exports'], function (exports) {
'use strict';
exports['default'] = Ember.HTMLBars.template((function() {
return {
...
buildFragment: function buildFragment(dom) {
var el0 = dom.createDocumentFragment();
var el1 = dom.createTextNode("Hello, ");
dom.appendChild(el0, el1);
var el1 = dom.createElement("strong");
var el2 = dom.createComment("");
dom.appendChild(el1, el2);
var el2 = dom.createTextNode(" ");
dom.appendChild(el1, el2);
var el2 = dom.createComment("");
dom.appendChild(el1, el2);
dom.appendChild(el0, el1);
var el1 = dom.createTextNode("!");
dom.appendChild(el0, el1);
return el0;
},
buildRenderNodes: function buildRenderNodes(dom, fragment,
contextualElement) {
var element0 = dom.childAt(fragment, [1]);
var morphs = new Array(2);
morphs[0] = dom.createMorphAt(element0,0,0);
morphs[1] = dom.createMorphAt(element0,2,2);
return morphs;
},
statements: [
["content","firstName",["loc",[null,[1,15],[1,28]]]],
["content","lastName",["loc",[null,[1,29],[1,41]]]]
],
...
};
}()));
});
LAZY LOADING
Lazy engines, JIT template compiler
Webpack chunking
Webpack chunking
SHADOW DOM
APPLICATION DOM
COMPONENT
SHADOW DOM
SHADOW DOM
ENCAPSULATION http://codepen.io/mike-north/pen/OPBNmq?editors=101
SHADOW DOM
CSS ENCAPSULATION
▸ CSS rules do not usually pierce
shadow roots
▸ CSS defined inside the shadow
root only affects that DOM
▸ :host pseudo-selector for
component boundary
▸ ::shadow penetrates shadow
roots
▸ /deep/ is deprecated
// Inside shadow root
:host {
border: 1px solid red;
}
:host(.hover) {
border: 1px solid blue;
}
// Outside shadow root
#my-thing::shadow p {
color: green;
}
HOW DO FRAMEWORKS
HANDLE THIS?
CSS ENCAPSULATION
ember-css-modules
ember-component-css
“component styles”
react-css-modules
SHADOW DOM
CONTENT PROJECTION
<textarea name="comment" id="" cols="30" >
This is the content of my textarea
</textarea>
SHADOW DOM
CONTENT PROJECTION
http://codepen.io/mike-north/pen/emPZMv?editors=1000
CONTENT PROJECTION
{{yield}}
2.x <ng-content>, 1.x ng-transclude
this.props.children
HTML IMPORTS
<link rel="import" href="myfile.html" />
<link rel="import" href="myfile.html" />
// Get document fragment of an import
var content = document
.querySelector('link[rel="import"]')
.import;
<link rel="import" href="myfile.html" />
// Get document fragment of an import
var content = document
.querySelector('link[rel="import"]')
.import;
// From a <script> included with the import,
// access imported DOM
var $abc = document
.currentScript
.ownerDocument
.querySelector('.abc');
HOW DO FRAMEWORKS
HANDLE THIS?
IMPORTING COMPONENTS
import MyComponent from 'my-component';
CUSTOM
ELEMENTS
CUSTOM ELEMENTS
WHY IS THIS IMPORTANT
▸ Composable building
blocks
▸ Life-cycle
▸ Extensibility
▸ Alignment of HTML with
mental model of your UI
CUSTOM ELEMENTS
REGISTERING
// Extend a DOM element prototype
var MegaButtonProto =
Object.create(HTMLButtonElement.prototype);
CUSTOM ELEMENTS
REGISTERING
// Extend a DOM element prototype
var MegaButtonProto =
Object.create(HTMLButtonElement.prototype);
// Register your new element type
var MegaButton =
document.registerElement("mega-button", {
prototype: MegaButtonProto,
extends: "button"
});
CUSTOM ELEMENTS
ADDING A TEMPLATE
// Template
var infoBoxTemplate = document.querySelector('#info-pane-template');
// Extend a DOM element
var InfoBoxProto = Object.create(HTMLElement.prototype, {
createdCallback: {
value: function() {
// Create the shadow root
this.createShadowRoot().appendChild(
// Add the template to the shadow root
document.importNode(infoBoxTemplate.content, true)
);
}
}
});
CUSTOM ELEMENTS
ADDING A TEMPLATE
// Template
var infoBoxTemplate = document.querySelector('#info-pane-template');
// Extend a DOM element
var InfoBoxProto = Object.create(HTMLElement.prototype, {
createdCallback: {
value: function() {
// Create the shadow root
this.createShadowRoot().appendChild(
// Add the template to the shadow root
document.importNode(infoBoxTemplate.content, true)
);
}
}
});
// Template
var infoBoxTemplate = document.querySelector('#info-pane-template');
// Extend a DOM element
var InfoBoxProto = Object.create(HTMLElement.prototype, {
createdCallback: {
value: function() {
// Create the shadow root
this.createShadowRoot().appendChild(
// Add the template to the shadow root
document.importNode(infoBoxTemplate.content, true)
);
}
}
});
CUSTOM ELEMENTS
ADDING A TEMPLATE
HOW DO FRAMEWORKS
HANDLE THIS?
CUSTOM ELEMENTS
Ember.Component
@Component
React.Component
DEMO
DEMO
•Starting Point: http://codepen.io/TrueNorth/pen/PwdLrj
•Componentized: http://codepen.io/TrueNorth/pen/xbyqME
•HTML Importified: http://codepen.io/TrueNorth/pen/ogawLm
THANKS!
michael.l.north@gmail.com @MICHAELLNORTH

The Road to Native Web Components