Post on 16-Jul-2015
transcript
1 / 145
Why you should be using WebComponents Now. And How.
PHIL @LEGGETTERHead of Evangelism
2 / 145
What we'll coverWhat are Web Components?State of native supportComponentised Web Apps nowWhy Web Components are the future!
3 / 145
What we'll coverWhat are Web Components?State of native supportComponentised Web Apps nowWhy Web Components are the future!♥ Eric Bidelman's Google IO 2014 talk ♥
4 / 145
What are Web Components?
5 / 145
What are Web Components?Custom ElementsHTML TemplatesShadow DOMHTML Imports
6 / 145
Custom Elements
7 / 145
<button>Click Me</button> Click Me
<input type="text" /><input type="number" /><input type="password" /> They all look the same
<select> <option>Select Me</option> <option>Dude</option></select>
Select Me
<label>Check Me</label><input type="checkbox" />
Check Me
Right Now - Elements
a, b, blockquote, body, br, code, div, em, fieldset, h1, h2, hr, img, li, ol, p, pre, span,strong, style, table, tr, td, ...
8 / 145
<header>I'm a header</header> Stuff around sections or grouping content e.g. main, nav,footer, figure, article, aside etc.
<progress />Form improvements e.g. meter, datalist, keygen,
output
<video />
Embedded content e.g. audio, canvas, svg, math
HTML5 Elements
9 / 145
Elements - Structure & Meaning<!doctype html><html> <head> <meta charset="utf-8" /> <title>HTML Elements</title> <meta name="description" content="" /> <link rel="stylesheet" href="css/stylez.css" /> </head> <body> <nav> <ul> <li><a href="#">Home</a></li> </ul> </nav> <header> <p>Hello world! This (part of) is HTML5 Boilerplate.</p> </header> <main> <article>Ohhhh. Interesting</article> </main> <footer>© me</footer> <script src="js/script.js"></script> </body></html>
10 / 145
Elements in "apps"
11 / 145
Elements. Arrrgghhh!
12 / 145
The Solution:
<Custom Elements />
13 / 145
The Solution:
<Custom Elements />
More than just markupIMHO the most important part of WebComponents
14 / 145
Custom Elements: A new Gmail<!doctype html><html> <head> <meta charset="utf-8"> <title>A new Gmail?</title> <meta name="description" content=""> </head> <body>
15 / 145
Custom Elements: A new Gmail<!doctype html><html> <head> <meta charset="utf-8"> <title>A new Gmail?</title> <meta name="description" content=""> </head> <body>
<header> <img src="img/logo.png" alt="Google Logo" /> <gmail-search /> <gmail-account-strip /> </header>
16 / 145
Custom Elements: A new Gmail<!doctype html><html> <head> <meta charset="utf-8"> <title>A new Gmail?</title> <meta name="description" content=""> </head> <body>
<header> <img src="img/logo.png" alt="Google Logo" /> <gmail-search /> <gmail-account-strip /> </header>
<gmail-side-bar> <nav is="gmail-labels"></nav> <gmail-contacts /> </gmail-sidebar>
17 / 145
Custom Elements: A new Gmail<!doctype html><html> <head> <meta charset="utf-8"> <title>A new Gmail?</title> <meta name="description" content=""> </head> <body>
<header> <img src="img/logo.png" alt="Google Logo" /> <gmail-search /> <gmail-account-strip /> </header>
<gmail-side-bar> <nav is="gmail-labels"></nav> <gmail-contacts /> </gmail-sidebar>
<main> <nav is="gmail-categories"></nav> <gmail-email-list /> </main>
18 / 145
Custom Elements: A new Gmail<!doctype html><html> <head> <meta charset="utf-8"> <title>A new Gmail?</title> <meta name="description" content=""> </head> <body>
<header> <img src="img/logo.png" alt="Google Logo" /> <gmail-search /> <gmail-account-strip /> </header>
<gmail-side-bar> <nav is="gmail-labels"></nav> <gmail-contacts /> </gmail-sidebar>
<main> <nav is="gmail-categories"></nav> <gmail-email-list /> </main>
<gmail-talk /> </body>
19 / 145
<img src="http://avatars.io/twitter/leggetter" />
<img src="http://avatars.io/facebook/leggetter" />
<img src="http://avatars.io/instagram/leggetter" />
<img src="http://avatars.io/gravatar/phil@pusher.com" />
Start Simple - An Avatar
20 / 145
Custom Elements
<my-avatar service="twitter" username="leggetter" />
21 / 145
Custom Elements
<my-avatar service="twitter" username="leggetter" />
<script>var MyAvatarPrototype = Object.create(HTMLElement.prototype);
22 / 145
Custom Elements
<my-avatar service="twitter" username="leggetter" />
<script>var MyAvatarPrototype = Object.create(HTMLElement.prototype);
MyAvatarPrototype.createdCallback = function() { var username = this.getAttribute('username'); var service = this.getAttribute('service');
23 / 145
Custom Elements
<my-avatar service="twitter" username="leggetter" />
<script>var MyAvatarPrototype = Object.create(HTMLElement.prototype);
MyAvatarPrototype.createdCallback = function() { var username = this.getAttribute('username'); var service = this.getAttribute('service');
var url = 'http://avatars.io/' + service + '/' + username;
24 / 145
Custom Elements
<my-avatar service="twitter" username="leggetter" />
<script>var MyAvatarPrototype = Object.create(HTMLElement.prototype);
MyAvatarPrototype.createdCallback = function() { var username = this.getAttribute('username'); var service = this.getAttribute('service');
var url = 'http://avatars.io/' + service + '/' + username;
var img = document.createElement( 'img' ); img.setAttribute('src', url); this.appendChild(img);};
25 / 145
Custom Elements
<my-avatar service="twitter" username="leggetter" />
<script>var MyAvatarPrototype = Object.create(HTMLElement.prototype);
MyAvatarPrototype.createdCallback = function() { var username = this.getAttribute('username'); var service = this.getAttribute('service');
var url = 'http://avatars.io/' + service + '/' + username;
var img = document.createElement( 'img' ); img.setAttribute('src', url); this.appendChild(img);};
document.registerElement('my-avatar', { prototype: MyAvatarPrototype});</script>
Define your own elements. 26 / 145
<my-avatar service="twitter" username="leggetter" />
<my-avatar service="facebook" username="leggetter" />
<my-avatar service="instagram" username="leggetter" />
<my-avatar service="twitter" username="garyshort" />
<my-avatar />
27 / 145
Custom Elements - Extending
<img is="my-avatar-ext" service="twitter" username="leggetter" />
28 / 145
Custom Elements - Extending
<img is="my-avatar-ext" service="twitter" username="leggetter" />
<script>var MyAvatarExtPrototype = Object.create(HTMLImageElement.prototype);
29 / 145
Custom Elements - Extending
<img is="my-avatar-ext" service="twitter" username="leggetter" />
<script>var MyAvatarExtPrototype = Object.create(HTMLImageElement.prototype);
MyAvatarExtPrototype.createdCallback = function() { var username = this.getAttribute('username'), service = this.getAttribute('service'), url = 'http://avatars.io/' + service + '/' + username;
this.setAttribute('src', url);};
30 / 145
Custom Elements - Extending
<img is="my-avatar-ext" service="twitter" username="leggetter" />
<script>var MyAvatarExtPrototype = Object.create(HTMLImageElement.prototype);
MyAvatarExtPrototype.createdCallback = function() { var username = this.getAttribute('username'), service = this.getAttribute('service'), url = 'http://avatars.io/' + service + '/' + username;
this.setAttribute('src', url);};
document.registerElement('my-avatar-ext', { prototype: MyAvatarExtPrototype, extends: 'img'});</script>
Extending existing elements31 / 145
Custom Elements - Lifecycle
createdCallbackattachedCallbackdetachedCallbackattributeChangedCallback(attrName,oldVal, newVal)
32 / 145
Create Elements usingJavaScript
<script>function createPhils() { var tooManyPhils = 104; var phils = 0; do { var el = document.createElement( 'my-avatar' ); el.setAttribute('service', 'twitter'); el.setAttribute('username', 'leggetter'); document.getElementById( 'phils' ).appendChild( el ); ++phils; } while( phils < tooManyPhils );}</script>
Create Phils
33 / 145
Why Custom Elements?
34 / 145
Templates
Native HTML TemplatingSupport
35 / 145
<script type="text/x-handlebars-template"> <div class="entry"> <h1>{{title}}</h1> <div>{{body}}</div> </div></script>
36 / 145
HTML Templates Create Avatar
<template id="my-avatar-template"> <style> .container { background-color: cyan; } <!-- omitted for brevity --> </style> <div class="container"> <img class="avatar" /> <span class="username"></span> <span class="service"></span> </div></template>
37 / 145
HTML Templates Create Avatar
<template id="my-avatar-template"> <style> .container { background-color: cyan; } <!-- omitted for brevity --> </style> <div class="container"> <img class="avatar" /> <span class="username"></span> <span class="service"></span> </div></template>
var MyAvatarTmplPrototype = Object.create(HTMLElement.prototype);
MyAvatarTmplPrototype.createdCallback = function() { // get attributes & build url
38 / 145
HTML Templates Create Avatar
<template id="my-avatar-template"> <style> .container { background-color: cyan; } <!-- omitted for brevity --> </style> <div class="container"> <img class="avatar" /> <span class="username"></span> <span class="service"></span> </div></template>
var MyAvatarTmplPrototype = Object.create(HTMLElement.prototype);
MyAvatarTmplPrototype.createdCallback = function() { // get attributes & build url
var content = document.querySelector( '#my-avatar-template' ).content; var el = document.importNode( content, true );
39 / 145
HTML Templates Create Avatar
<template id="my-avatar-template"> <style> .container { background-color: cyan; } <!-- omitted for brevity --> </style> <div class="container"> <img class="avatar" /> <span class="username"></span> <span class="service"></span> </div></template>
var MyAvatarTmplPrototype = Object.create(HTMLElement.prototype);
MyAvatarTmplPrototype.createdCallback = function() { // get attributes & build url
var content = document.querySelector( '#my-avatar-template' ).content; var el = document.importNode( content, true );
el.querySelector( '.avatar' ).setAttribute( 'src', url ); el.querySelector( '.username' ).textContent = username; el.querySelector( '.service' ).textContent = service; this.appendChild( el );};
40 / 145
HTML Templates Create Avatar
<template id="my-avatar-template"> <style> .container { background-color: cyan; } <!-- omitted for brevity --> </style> <div class="container"> <img class="avatar" /> <span class="username"></span> <span class="service"></span> </div></template>
var MyAvatarTmplPrototype = Object.create(HTMLElement.prototype);
MyAvatarTmplPrototype.createdCallback = function() { // get attributes & build url
var content = document.querySelector( '#my-avatar-template' ).content; var el = document.importNode( content, true );
el.querySelector( '.avatar' ).setAttribute( 'src', url ); el.querySelector( '.username' ).textContent = username; el.querySelector( '.service' ).textContent = service; this.appendChild( el );};
document.registerElement('my-avatar-tmpl', { prototype: MyAvatarTmplPrototype
41 / 145
Why native HTML Templates?
Libraries → NativeNative benefitsDocument fragment = lightweightInert until cloned/used
42 / 145
Shadow DOM
DOM/CSS "scoping" /protection
43 / 145
Shadow DOM - Already usingit
44 / 145
Shadow DOM SubTrees
Light DOMShadow DOMComposed (rendered) DOM
45 / 145
Light DOM
<my-custom-element> <q>Hello World</q> <!-- part of my-custom-element's light DOM --></my-custom-element>
46 / 145
Shadow DOM
#document-fragment <!-- everything in here is my-custom-element's shadow DOM --> <span>People say: <content></content></span> <footer>sometimes</footer>
47 / 145
Rendered DOM
<my-custom-element> <span>People say: <q>Hello World</q></span> <footer>sometimes</footer></my-custom-element>
48 / 145
Shadow DOM - Problems itsolves
49 / 145
Styles <span class="container">Bleed!</span>
<template id="my-avatar-tmpl"> <style> .container { background-color: cyan; } ...
<my-avatar-tmpl service="twitter" username="leggetter" />
Styles Bleed!
Me
Shadow DOM - Problems it solves
50 / 145
Styles <span class="container">Bleed!</span>
<template id="my-avatar-tmpl"> <style> .container { background-color: cyan; } ...
<my-avatar-tmpl service="twitter" username="leggetter" />
Styles Bleed!
Me
<template id="my-avatar-template"> <div class="container"> <img id="avatar" /> ...</template>
Global DOM e.g. id
attributes
Shadow DOM - Problems it solves
51 / 145
Shadow DOM - In Action Create DevWeek
52 / 145
Shadow DOM - In Action Create DevWeek
<template id="my-avatar-shadow-tmpl"> <style> .container { background-color: red; color: white; } ... </style> <div class="container"> <img id="avatar" /> ... </div></template>
53 / 145
Shadow DOM - In Action Create DevWeek
<template id="my-avatar-shadow-tmpl"> <style> .container { background-color: red; color: white; } ... </style> <div class="container"> <img id="avatar" /> ... </div></template>
var MyAvatarShadowPrototype = Object.create(HTMLElement.prototype);
MyAvatarShadowPrototype.createdCallback = function() { // get attributes & build url
var content = document.querySelector( '#my-avatar-shadow-tmpl' ).content;
54 / 145
Shadow DOM - In Action Create DevWeek
<template id="my-avatar-shadow-tmpl"> <style> .container { background-color: red; color: white; } ... </style> <div class="container"> <img id="avatar" /> ... </div></template>
var MyAvatarShadowPrototype = Object.create(HTMLElement.prototype);
MyAvatarShadowPrototype.createdCallback = function() { // get attributes & build url
var content = document.querySelector( '#my-avatar-shadow-tmpl' ).content;
this.shadow = this.createShadowRoot(); this.shadow.appendChild( document.importNode( content, true ) );
55 / 145
Shadow DOM - In Action Create DevWeek
<template id="my-avatar-shadow-tmpl"> <style> .container { background-color: red; color: white; } ... </style> <div class="container"> <img id="avatar" /> ... </div></template>
var MyAvatarShadowPrototype = Object.create(HTMLElement.prototype);
MyAvatarShadowPrototype.createdCallback = function() { // get attributes & build url
var content = document.querySelector( '#my-avatar-shadow-tmpl' ).content;
this.shadow = this.createShadowRoot(); this.shadow.appendChild( document.importNode( content, true ) );
this.shadow.querySelector( '#avatar' ).setAttribute( 'src', url ); this.shadow.querySelector( '#username' ).textContent = username; this.shadow.querySelector( '#service' ).textContent = service;};
56 / 145
Shadow DOM - In Action Create DevWeek
<template id="my-avatar-shadow-tmpl"> <style> .container { background-color: red; color: white; } ... </style> <div class="container"> <img id="avatar" /> ... </div></template>
var MyAvatarShadowPrototype = Object.create(HTMLElement.prototype);
MyAvatarShadowPrototype.createdCallback = function() { // get attributes & build url
var content = document.querySelector( '#my-avatar-shadow-tmpl' ).content;
this.shadow = this.createShadowRoot(); this.shadow.appendChild( document.importNode( content, true ) );
this.shadow.querySelector( '#avatar' ).setAttribute( 'src', url ); this.shadow.querySelector( '#username' ).textContent = username; this.shadow.querySelector( '#service' ).textContent = service;};
document.registerElement('my-avatar-shadow', { prototype: MyAvatarShadowPrototype
57 / 145
Why Shadow DOM?
DOM & CSS ScopingProtection for all: Page and ElementEncapsulation
58 / 145
HTML Imports
Loading & DependencyManagement
59 / 145
HTML Imports - Example
Before
<link rel="stylesheet" href="bootstrap.css" /><link rel="stylesheet" href="fonts.css" /><script src="jquery.js"></script><script src="bootstrap.js"></script><script src="bootstrap-tooltip.js"></script><script src="bootstrap-dropdown.js"></script>
60 / 145
HTML Imports - Example
Before
<link rel="stylesheet" href="bootstrap.css" /><link rel="stylesheet" href="fonts.css" /><script src="jquery.js"></script><script src="bootstrap.js"></script><script src="bootstrap-tooltip.js"></script><script src="bootstrap-dropdown.js"></script>
After
<link rel="import" href="bootstrap.html" />
61 / 145
HTML Imports - Compositionteam-pusher.html
62 / 145
HTML Imports - Compositionteam-pusher.html
<link rel="import" href="my-avatar-import.html" />
63 / 145
HTML Imports - Compositionteam-pusher.html
<link rel="import" href="my-avatar-import.html" />
<template id="team-pusher-tmpl"> <style> </style>
<my-avatar-import service="twitter" username="maxthelion" /> <my-avatar-import service="twitter" username="copypastaa" /> ... <my-avatar-import service="twitter" username="leggetter" /></template>
...
64 / 145
HTML Imports - Compositionteam-pusher.html
<link rel="import" href="my-avatar-import.html" />
<template id="team-pusher-tmpl"> <style> </style>
<my-avatar-import service="twitter" username="maxthelion" /> <my-avatar-import service="twitter" username="copypastaa" /> ... <my-avatar-import service="twitter" username="leggetter" /></template>
<script> var TeamPusherPrototype = Object.create(HTMLElement.prototype);
TeamPusherPrototype.createdCallback = function() { // Get template, createShadowRoot etc. };
document.registerElement('dunddd-organisers', { prototype: TeamPusherPrototype });</script>
...
65 / 145
HTML Imports - CompositionDemo
<link rel="import" href="assets/team-pusher.html" />
<team-pusher></team-pusher>
66 / 145
HTML Imports - CompositionDemo
<link rel="import" href="assets/team-pusher.html" />
<team-pusher></team-pusher>
maxtheliontwitter
copypastaatwitter
zimbatmtwitter
loicdumastwitter
mdpyetwitter
olga_dukovatwitter
pawel_ledwontwitter
hamchapmantwitter
rumblesantwitter
jamessiddletwitter
willsewell_twitter
leggettertwitter
67 / 145
HTML Imports - Gotchas /Patterns!
68 / 145
Get & use document from thecurrentScript
( function( currentScript ) {
var ownerDoc = currentScript.ownerDocument;
} )( document._currentScript || document.currentScript );
69 / 145
importNode and NOTcloneNode for Template
// Note: use ownerDocvar content = ownerDoc.querySelector( '#my-template' );
var clone = ownerDoc.importNode( content, true );
70 / 145
You can't <link> into the ShadowDOM
<template> <link rel="stylesheet" href="path/to/style.css" /></template>
71 / 145
Why Use HTML Imports?Bundle JS/HTML/CSS → single URLBasic dependency management
Sharing & reuseSupports composition
72 / 145
State of Native Support
73 / 145
Browsers
Chrome41
Firefox36
Safari 8 IE 10
CustomElements
Y N* N N
Templates Y Y Y N
Shadow DOM Y N* N N
HTML Imports Y N* N N
* Can be enabled in config
74 / 145
Firefox
https://hacks.mozilla.org/2014/12/mozilla-and-web-components/
“ Mozilla will not ship animplementation of HTML Imports.We expect that once JavaScriptmodules ... is shipped, the way welook at this problem will havechanged.
75 / 145
77 / 145
IE UserVoice
Safari?
78 / 145
All is not Lost
79 / 145
Browsers - with PolyfillsChrome
42Firefox
36Safari 8 IE 10
CustomElements
Y Y Y Y
Templates Y Y Y Y
Shadow DOM Y Y* Y* Y*
HTML Imports Y Y Y Y
http://webcomponents.org/polyfills
* Shadow DOM Polyfill limitations80 / 145
Componentised Web Appsnow
81 / 145
Componentised Web Appsnow - questions?
Should native browser support stop us thinking aboutbuilding componentised web apps?
82 / 145
Componentised Web Appsnow - questions?
Should native browser support stop us thinking aboutbuilding componentised web apps?
No!
83 / 145
Componentised Web Appsnow - questions?
Should native browser support stop us thinking aboutbuilding componentised web apps?
No!
Should we be build componentised web apps anyway?
84 / 145
Componentised Web Appsnow - questions?
Should native browser support stop us thinking aboutbuilding componentised web apps?
No!
Should we be build componentised web apps anyway?
We're already building web apps out ofcomponents right now!
85 / 145
JavaScript
Libraries & Frameworks
86 / 145
AngularJS
87 / 145
AngularJS<script src="js/angular.min.js"></script>
88 / 145
AngularJS<script src="js/angular.min.js"></script>
<script>angular.module('demo', []) .directive('ngAvatar', function () { return {
89 / 145
AngularJS<script src="js/angular.min.js"></script>
<script>angular.module('demo', []) .directive('ngAvatar', function () { return {
restrict:"AEC",
90 / 145
AngularJS<script src="js/angular.min.js"></script>
<script>angular.module('demo', []) .directive('ngAvatar', function () { return {
restrict:"AEC",
scope: { service: '@', username: '@' },
91 / 145
AngularJS<script src="js/angular.min.js"></script>
<script>angular.module('demo', []) .directive('ngAvatar', function () { return {
restrict:"AEC",
scope: { service: '@', username: '@' },
template: '<img src="http://avatars.io/' + '{{service}}/{{username}}" />' }; });</script>
<body ng-app="demo">
92 / 145
AngularJS<script src="js/angular.min.js"></script>
<script>angular.module('demo', []) .directive('ngAvatar', function () { return {
restrict:"AEC",
scope: { service: '@', username: '@' },
template: '<img src="http://avatars.io/' + '{{service}}/{{username}}" />' }; });</script>
<body ng-app="demo">
<ng-avatar service="twitter" username="leggetter" />
93 / 145
AngularJS<script src="js/angular.min.js"></script>
<script>angular.module('demo', []) .directive('ngAvatar', function () { return {
restrict:"AEC",
scope: { service: '@', username: '@' },
template: '<img src="http://avatars.io/' + '{{service}}/{{username}}" />' }; });</script>
<body ng-app="demo">
<ng-avatar service="twitter" username="leggetter" />
94 / 145
EmberJS
95 / 145
EmberJS<script src="js/jquery-1.10.0.min.js"></script>
96 / 145
EmberJS<script src="js/jquery-1.10.0.min.js"></script>
<script src="js/handlebars.js"></script>
97 / 145
EmberJS<script src="js/jquery-1.10.0.min.js"></script>
<script src="js/handlebars.js"></script>
<script src="js/ember.js"></script>
98 / 145
EmberJS<script src="js/jquery-1.10.0.min.js"></script>
<script src="js/handlebars.js"></script>
<script src="js/ember.js"></script>
<script> var App = Ember.Application.create();
App.EmAvatarComponent = Ember.Component.extend({
99 / 145
EmberJS<script src="js/jquery-1.10.0.min.js"></script>
<script src="js/handlebars.js"></script>
<script src="js/ember.js"></script>
<script> var App = Ember.Application.create();
App.EmAvatarComponent = Ember.Component.extend({
url: function () { return 'http://avatars.io/' + this.get( 'service' ) + '/' + this.get( 'username' ); }.property( 'username' , 'service' ) });</script>
100 / 145
EmberJS<script src="js/jquery-1.10.0.min.js"></script>
<script src="js/handlebars.js"></script>
<script src="js/ember.js"></script>
<script> var App = Ember.Application.create();
App.EmAvatarComponent = Ember.Component.extend({
url: function () { return 'http://avatars.io/' + this.get( 'service' ) + '/' + this.get( 'username' ); }.property( 'username' , 'service' ) });</script>
<script type="text/x-handlebars" id="components/em-avatar"> <img {{bind-attr src=url}} /></script>
101 / 145
EmberJS<script src="js/jquery-1.10.0.min.js"></script>
<script src="js/handlebars.js"></script>
<script src="js/ember.js"></script>
<script> var App = Ember.Application.create();
App.EmAvatarComponent = Ember.Component.extend({
url: function () { return 'http://avatars.io/' + this.get( 'service' ) + '/' + this.get( 'username' ); }.property( 'username' , 'service' ) });</script>
<script type="text/x-handlebars" id="components/em-avatar"> <img {{bind-attr src=url}} /></script>
<script type="text/x-handlebars"> {{em-avatar service="twitter" username="leggetter"}}</script>
http://jsbin.com/fexawujibe/2/edit?html,output102 / 145
ReactJS<script src="js/react.js"></script><script src="js/JSXTransformer.js"></script>
104 / 145
ReactJS<script src="js/react.js"></script><script src="js/JSXTransformer.js"></script>
<script type="text/jsx">var ReAvatar = React.createClass({ render: function() { return ( <img src={"http://avatars.io/" + this.props.service + "/" + this.props.username} /> ); }});
105 / 145
ReactJS<script src="js/react.js"></script><script src="js/JSXTransformer.js"></script>
<script type="text/jsx">var ReAvatar = React.createClass({ render: function() { return ( <img src={"http://avatars.io/" + this.props.service + "/" + this.props.username} /> ); }});
React.renderComponent( <ReAvatar service="twitter" username="leggetter" />, document.querySelector('re-avatar'));</script>
106 / 145
ReactJS<script src="js/react.js"></script><script src="js/JSXTransformer.js"></script>
<script type="text/jsx">var ReAvatar = React.createClass({ render: function() { return ( <img src={"http://avatars.io/" + this.props.service + "/" + this.props.username} /> ); }});
React.renderComponent( <ReAvatar service="twitter" username="leggetter" />, document.querySelector('re-avatar'));</script>
<re-avatar />
107 / 145
ReactJS<script src="js/react.js"></script><script src="js/JSXTransformer.js"></script>
<script type="text/jsx">var ReAvatar = React.createClass({ render: function() { return ( <img src={"http://avatars.io/" + this.props.service + "/" + this.props.username} /> ); }});
React.renderComponent( <ReAvatar service="twitter" username="leggetter" />, document.querySelector('re-avatar'));</script>
<re-avatar />
108 / 145
Many More...
KnockoutJS ComponentsBackbone componentsBackbone with React componentsCanJS components
And...
109 / 145
110 / 145
Polymer
<script src="webcomponentsjs/webcomponents.min.js"></script><link rel="import" href="polymer/polymer.html">
111 / 145
Polymer
<script src="webcomponentsjs/webcomponents.min.js"></script><link rel="import" href="polymer/polymer.html">
<polymer-element name="po-avatar" attributes="service username">
112 / 145
Polymer
<script src="webcomponentsjs/webcomponents.min.js"></script><link rel="import" href="polymer/polymer.html">
<polymer-element name="po-avatar" attributes="service username">
<template> <img src="http://avatars.io/{{service}}/{{username}}" /> </template>
113 / 145
Polymer
<script src="webcomponentsjs/webcomponents.min.js"></script><link rel="import" href="polymer/polymer.html">
<polymer-element name="po-avatar" attributes="service username">
<template> <img src="http://avatars.io/{{service}}/{{username}}" /> </template>
<script> Polymer('po-avatar', {}); </script></polymer-element>
114 / 145
Polymer
<script src="webcomponentsjs/webcomponents.min.js"></script><link rel="import" href="polymer/polymer.html">
<polymer-element name="po-avatar" attributes="service username">
<template> <img src="http://avatars.io/{{service}}/{{username}}" /> </template>
<script> Polymer('po-avatar', {}); </script></polymer-element>
<po-avatar service="facebook" username="leggetter" />
115 / 145
Polymer
<script src="webcomponentsjs/webcomponents.min.js"></script><link rel="import" href="polymer/polymer.html">
<polymer-element name="po-avatar" attributes="service username">
<template> <img src="http://avatars.io/{{service}}/{{username}}" /> </template>
<script> Polymer('po-avatar', {}); </script></polymer-element>
<po-avatar service="facebook" username="leggetter" />
116 / 145
117 / 145
118 / 145
Who's Building Componentised WebApps now?
119 / 145
Who's Building Componentised WebApps now?
Angular, Ember, Backbone, Knockout, React, Web Components withPolyfills, Polymer
You probably are already<ng-avatar service="twitter" username="leggetter" />
vs.
<my-avatar service="twitter" username="leggetter" />
120 / 145
Examples
From Eric's SlidesGitHubChrome OS
GMail built in PolymerTopeka game built in Polymer
121 / 145
Why Web Components arethe future!
122 / 145
1. You're already buildingcomponentised web apps
If you're not, you probablyshould be
123 / 145
2. Trends & Demand
124 / 145
Libraries
Alignment toward Web ComponentsAngular - DirectivesEmber - ComponentsKnockout - ComponentsPolymer - build upon Web ComponentsAngular 2...
125 / 145
126 / 145
Browser Vendor Support
GoogleOpera - uses BlinkMozillaMicrosoft - ?
previously: HTA & ASP.NET ControlsIn high demand on IE UserVoice
Apple - ?
127 / 145
128 / 145
In Demand
3. Encourages good softwaredevelopment
Component-basedDevelopment
129 / 145
Separation of Concerns<!doctype html><html> <head> <meta charset="utf-8"> <title>A new Gmail?</title> <meta name="description" content=""> </head> <body>
<header> <img src="img/logo.png" alt="Google Logo" /> <gmail-search /> <gmail-account-strip /> </header>
<gmail-side-bar> <nav is="gmail-labels"></nav> <gmail-contacts /> </gmail-sidebar> <main> <nav is="gmail-categories"></nav> <gmail-email-list /> </main>
<gmail-talk /> </body></html>
130 / 145
Encapsulation
Shadow DOM - Style & DOM encapsulationDoes NOT offer JavaScript protection
Hacky Custom Element
leggettertwitter
Don't click me!
131 / 145
Loose Coupling
Custom EventsElement API (interface)Existing messaging frameworks
132 / 145
Custom Events<script> var CustomEventPrototype = Object.create(HTMLElement.prototype); CustomEventPrototype.createdCallback = function() { // Build element ...
this.addEventListener('click', function() { var customEvent = new CustomEvent('cheese'); this.dispatchEvent(customEvent); }.bind(this)); };
// ...
133 / 145
Custom Events<script> var CustomEventPrototype = Object.create(HTMLElement.prototype); CustomEventPrototype.createdCallback = function() { // Build element ...
this.addEventListener('click', function() { var customEvent = new CustomEvent('cheese'); this.dispatchEvent(customEvent); }.bind(this)); };
// ...
var customEl = document.getElementById('my_custom_ev'); customEl.addEventListener('cheese', function() { alert('cheese fired!'); });</script>
<custom-event-ex id="my_custom_ev"></custom-event-ex>
134 / 145
Element API Attributes & Methods<script> CustomEventPrototype.startSpin = function() { this.img.classList.toggle('spin'); };
CustomEventPrototype.stopSpin = function() { this.img.classList.toggle('spin'); };
// ...
var spinEl = document.getElementById('spin_el'); spinEl.startSpin();
// ...
spinEl.stopSpin();</script>
<custom-event-ex id="spin_el"></custom-event-ex>
135 / 145
Supports Change
136 / 145
Reusability
<link rel="import" href="https://some-cdn.com/my-avatar.html" />
137 / 145
High Cohesion
myavatar.html├── js/script.js├── css/styles.css└── img/bg.png
138 / 145
Problems? Solved in thefuture?
HTML ImportsVulcanize | HTTP2
Shared scripts?Cache
Multiple versions?Better Cross-component communication?Allow <link> for CSS in Shadow DOM?
139 / 145
SummaryCustom Elements - Awesome
140 / 145
Summary
Custom Elements - Awesome
HTML Templates, Shadow DOM, HTML Imports - Native FTW
141 / 145
Summary
Custom Elements - Awesome
HTML Templates, Shadow DOM, HTML Imports - Native FTW
You can & are building componentised web apps now
142 / 145
Summary
Custom Elements - Awesome
HTML Templates, Shadow DOM, HTML Imports - Native FTW
You can & are building componentised web apps now
Trends & "best practice" ♥ Web Components
143 / 145
Summary
Custom Elements - Awesome
HTML Templates, Shadow DOM, HTML Imports - Native FTW
You can & are building componentised web apps now
Trends & "best practice" ♥ Web Components
Web Components are the future!
144 / 145
Why you should be using WebComponents Now. And How.
Questions?
leggetter.github.io/web-components-now
PHIL @LEGGETTERHead of Evangelism
145 / 145