SlideShare a Scribd company logo
1 / 145
Why you should be using Web
Components Now. And How.
PHIL @LEGGETTER
Head of Evangelism
2 / 145
What we'll cover
What are Web Components?
State of native support
Componentised Web Apps now
Why Web Components are the future!
3 / 145
What we'll cover
What are Web Components?
State of native support
Componentised Web Apps now
Why 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 Elements
HTML Templates
Shadow DOM
HTML 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, asideetc.
<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>&copy; 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 markup
IMHO the most important part of Web
Components
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 elements
31 / 145
Custom Elements - Lifecycle
createdCallback
attachedCallback
detachedCallback
attributeChangedCallback(attrName,
oldVal, newVal)
32 / 145
Create Elements using
JavaScript
<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 Templating
Support
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', { 41 / 145
Why native HTML Templates?
Libraries → Native
Native benefits
Document fragment = lightweight
Inert until cloned/used
42 / 145
Shadow DOM
DOM/CSS "scoping" /
protection
43 / 145
Shadow DOM - Already using
it
44 / 145
Shadow DOM SubTrees
Light DOM
Shadow DOM
Composed (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 it
solves
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', { 57 / 145
Why Shadow DOM?
DOM & CSS Scoping
Protection for all: Page and Element
Encapsulation
58 / 145
HTML Imports
Loading & Dependency
Management
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 - Composition
team-pusher.html
62 / 145
HTML Imports - Composition
team-pusher.html
<link rel="import" href="my-avatar-import.html" />
63 / 145
HTML Imports - Composition
team-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 - Composition
team-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 - Composition
Demo
<link rel="import" href="assets/team-pusher.html" />
<team-pusher></team-pusher>
66 / 145
HTML Imports - Composition
Demo
<link rel="import" href="assets/team-pusher.html" />
<team-pusher></team-pusher>
maxthelion
twitter
copypastaa
twitter
zimbatm
twitter
loicdumas
twitter
mdpye
twitter
olga_dukova
twitter
pawel_ledwon
twitter
hamchapman
twitter
rumblesan
twitter
jamessiddle
twitter
willsewell_
twitter
leggetter
twitter
67 / 145
HTML Imports - Gotchas /
Patterns!
68 / 145
Get & use documentfrom the
currentScript
( function( currentScript ) {
var ownerDoc = currentScript.ownerDocument;
} )( document._currentScript || document.currentScript );
69 / 145
importNodeand NOT
cloneNodefor Template
// Note: use ownerDoc
var content = ownerDoc.querySelector( '#my-template' );
var clone = ownerDoc.importNode( content, true );
70 / 145
You can't <link>into the Shadow
DOM
<template>
<link rel="stylesheet" href="path/to/style.css" />
</template>
71 / 145
Why Use HTML Imports?
Bundle JS/HTML/CSS → single URL
Basic dependency management
Sharing & reuse
Supports composition
72 / 145
State of Native Support
73 / 145
Browsers
Chrome
41
Firefox
36
Safari 8 IE 10
Custom
Elements
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-
“Mozilla will not ship an
implementation of HTML Imports.
We expect that once JavaScript
modules ... is shipped, the way we
look at this problem will have
changed.
75 / 145
Internet Explorer
76 / 145
77 / 145
IE UserVoice
Safari?
78 / 145
All is not Lost
79 / 145
Browsers - with Polyfills
Chrome
42
Firefox
36
Safari 8 IE 10
Custom
Elements
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 limitations
80 / 145
Componentised Web Apps
now
81 / 145
Componentised Web Apps
now - questions?
Should native browser support stop us thinking about
building componentised web apps?
82 / 145
Componentised Web Apps
now - questions?
Should native browser support stop us thinking about
building componentised web apps?
No!
83 / 145
Componentised Web Apps
now - questions?
Should native browser support stop us thinking about
building componentised web apps?
No!
Should we be build componentised web apps anyway?
84 / 145
Componentised Web Apps
now - questions?
Should native browser support stop us thinking about
building componentised web apps?
No!
Should we be build componentised web apps anyway?
We're already building web apps out of
components 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>
102 / 145
ReactJS
103 / 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 Components
Backbone components
Backbone with React components
CanJS 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 Web
Apps now?
119 / 145
Who's Building Componentised Web
Apps now?
Angular, Ember, Backbone, Knockout, React, Web Components with
Polyfills, Polymer
You probably are already
<ng-avatar service="twitter" username="leggetter" />
vs.
<my-avatar service="twitter" username="leggetter" />
120 / 145
Examples
From Eric's Slides
GitHub
Chrome OS
GMail built in Polymer
Topeka game built in Polymer
121 / 145
Why Web Components are
the future!
122 / 145
1. You're already building
componentised web apps
If you're not, you probably
should be
123 / 145
2. Trends & Demand
124 / 145
Libraries
Alignment toward Web Components
Angular - Directives
Ember - Components
Knockout - Components
Polymer - build upon Web Components
Angular 2...
125 / 145
126 / 145
Browser Vendor Support
Google
Opera - uses Blink
Mozilla
Microsoft - ?
previously: HTA & ASP.NET Controls
In high demand on IE UserVoice
Apple - ?
127 / 145
128 / 145
In Demand
3. Encourages good software
development
Component-based
Development
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> 130 / 145
Encapsulation
Shadow DOM - Style & DOM encapsulation
Does NOT offer JavaScript protection
Hacky Custom Element
leggetter
twitter
Don't click me!
131 / 145
Loose Coupling
Custom Events
Element 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 the
future?
HTML Imports
Vulcanize | HTTP2
Shared scripts?
Cache
Multiple versions?
Better Cross-component communication?
Allow <link>for CSS in Shadow DOM?
139 / 145
Summary
Custom 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 Web
Components Now. And How.
Questions?
leggetter.github.io/web-components-now
PHIL @LEGGETTER
Head of Evangelism
145 / 145

More Related Content

What's hot (20)

PDF
Unlock the next era of UI design with Polymer
Rob Dodson
 
PDF
Api
randyhoyt
 
PPTX
Iasi code camp 12 october 2013 shadow dom - mihai bîrsan
Codecamp Romania
 
PDF
AtlasCamp 2015: Web technologies you should be using now
Atlassian
 
PDF
Seven deadly theming sins
George Stephanis
 
PDF
AtlasCamp 2015: Connect everywhere - Cloud and Server
Atlassian
 
ODP
Fixture Replacement Plugins
Paul Klipp
 
PPTX
HTML5 Accessibility
bgibson
 
PDF
Introduction to jQuery Mobile - Web Deliver for All
Marc Grabanski
 
PDF
Web Components & Polymer 1.0 (Webinale Berlin)
Hendrik Ebbers
 
DOCX
Javascript
Jitendra Negi
 
PPTX
Introduction to HTML5 & CSS3
Pradeep Varadaraja Banavara
 
PDF
Polymer 1.0
Cyril Balit
 
PDF
Web UI performance tuning
Andy Pemberton
 
PPT
WordCamp Detroit 2010 Wordpress Theme Hacks
John Pratt
 
PDF
Image Manipulation in WordPress 3.5 - WordCamp Phoenix 2013
GetSource
 
KEY
Presenters in Rails
Mike Desjardins
 
PDF
HTML5 and CSS3 Shizzle
Chris Mills
 
PPT
Ajax
wangjiaz
 
Unlock the next era of UI design with Polymer
Rob Dodson
 
Iasi code camp 12 october 2013 shadow dom - mihai bîrsan
Codecamp Romania
 
AtlasCamp 2015: Web technologies you should be using now
Atlassian
 
Seven deadly theming sins
George Stephanis
 
AtlasCamp 2015: Connect everywhere - Cloud and Server
Atlassian
 
Fixture Replacement Plugins
Paul Klipp
 
HTML5 Accessibility
bgibson
 
Introduction to jQuery Mobile - Web Deliver for All
Marc Grabanski
 
Web Components & Polymer 1.0 (Webinale Berlin)
Hendrik Ebbers
 
Javascript
Jitendra Negi
 
Introduction to HTML5 & CSS3
Pradeep Varadaraja Banavara
 
Polymer 1.0
Cyril Balit
 
Web UI performance tuning
Andy Pemberton
 
WordCamp Detroit 2010 Wordpress Theme Hacks
John Pratt
 
Image Manipulation in WordPress 3.5 - WordCamp Phoenix 2013
GetSource
 
Presenters in Rails
Mike Desjardins
 
HTML5 and CSS3 Shizzle
Chris Mills
 
Ajax
wangjiaz
 

Similar to Why you should be using Web Components. And How - DevWeek 2015 (20)

PPTX
Magic of web components
HYS Enterprise
 
PPT
Reaching for the Future with Web Components and Polymer
FITC
 
PDF
Webcomponents TLV October 2014
Dmitry Bakaleinik
 
PDF
Web components are the future of the web - Take advantage of new web technolo...
Marios Fakiolas
 
PDF
Build Reusable Web Components using HTML5 Web cComponents
Gil Fink
 
PDF
Webcomponents v2
Dmitry Bakaleinik
 
PPTX
Web Components
FITC
 
PPTX
An Introduction to Web Components
Red Pill Now
 
PDF
Web components the future is here
Gil Fink
 
PDF
The Time for Vanilla Web Components has Arrived
Gil Fink
 
PPTX
Web Components: back to the future
DA-14
 
PDF
Web Component
偉格 高
 
PDF
Web Components - The Future is Here
Gil Fink
 
PPTX
Web Components Revolution
Web Standards School
 
PPTX
Web Components: Introduction and Practical Use Cases
sumitamar
 
PDF
Introduction to Web Components
Rich Bradshaw
 
PPTX
Web components
Mohd Saeed
 
PPTX
Web Components
FITC
 
PDF
Brave new world of HTML5 - Interlink Conference Vancouver 04.06.2011
Patrick Lauke
 
PDF
Web components - The Future is Here
Gil Fink
 
Magic of web components
HYS Enterprise
 
Reaching for the Future with Web Components and Polymer
FITC
 
Webcomponents TLV October 2014
Dmitry Bakaleinik
 
Web components are the future of the web - Take advantage of new web technolo...
Marios Fakiolas
 
Build Reusable Web Components using HTML5 Web cComponents
Gil Fink
 
Webcomponents v2
Dmitry Bakaleinik
 
Web Components
FITC
 
An Introduction to Web Components
Red Pill Now
 
Web components the future is here
Gil Fink
 
The Time for Vanilla Web Components has Arrived
Gil Fink
 
Web Components: back to the future
DA-14
 
Web Component
偉格 高
 
Web Components - The Future is Here
Gil Fink
 
Web Components Revolution
Web Standards School
 
Web Components: Introduction and Practical Use Cases
sumitamar
 
Introduction to Web Components
Rich Bradshaw
 
Web components
Mohd Saeed
 
Web Components
FITC
 
Brave new world of HTML5 - Interlink Conference Vancouver 04.06.2011
Patrick Lauke
 
Web components - The Future is Here
Gil Fink
 
Ad

More from Phil Leggetter (20)

PDF
An Introduction to AAARRRP: A framework for Defining Your Developer Relations...
Phil Leggetter
 
PDF
How APIs Enable Contextual Communications
Phil Leggetter
 
PDF
An Introduction to the AAARRRP Developer Relations Strategy Framework and How...
Phil Leggetter
 
PDF
An Introduction to the AAARRRP Developer Relations Strategy Framework and How...
Phil Leggetter
 
PDF
Contextual Communications: What, Why and How? Bristol JS
Phil Leggetter
 
PDF
Real-Time Web Apps & .NET. What Are Your Options? NDC Oslo 2016
Phil Leggetter
 
PDF
Real-Time Web Apps & .NET - What are your options?
Phil Leggetter
 
PDF
The Past, Present and Future of Real-Time Apps and Communications
Phil Leggetter
 
PDF
The Past, Present and Future of Real-Time Apps and Communications
Phil Leggetter
 
PDF
What's the ROI of Developer Relations?
Phil Leggetter
 
PDF
Real-Time Web Apps & Symfony. What are your options?
Phil Leggetter
 
PDF
Real-Time Web Apps in 2015 & Beyond
Phil Leggetter
 
PDF
Patterns and practices for building enterprise-scale HTML5 apps
Phil Leggetter
 
PDF
Fed London - January 2015
Phil Leggetter
 
PDF
How to Build Single Page HTML5 Apps that Scale
Phil Leggetter
 
PDF
Realtime Web Apps in 2014 & Beyond
Phil Leggetter
 
PDF
BladeRunnerJS Show & Tell
Phil Leggetter
 
PDF
Testing Ginormous JavaScript Apps - ScotlandJS 2014
Phil Leggetter
 
PDF
How to Build Front-End Web Apps that Scale - FutureJS
Phil Leggetter
 
PDF
Using BladeRunnerJS to Build Front-End Apps that Scale - Fluent 2014
Phil Leggetter
 
An Introduction to AAARRRP: A framework for Defining Your Developer Relations...
Phil Leggetter
 
How APIs Enable Contextual Communications
Phil Leggetter
 
An Introduction to the AAARRRP Developer Relations Strategy Framework and How...
Phil Leggetter
 
An Introduction to the AAARRRP Developer Relations Strategy Framework and How...
Phil Leggetter
 
Contextual Communications: What, Why and How? Bristol JS
Phil Leggetter
 
Real-Time Web Apps & .NET. What Are Your Options? NDC Oslo 2016
Phil Leggetter
 
Real-Time Web Apps & .NET - What are your options?
Phil Leggetter
 
The Past, Present and Future of Real-Time Apps and Communications
Phil Leggetter
 
The Past, Present and Future of Real-Time Apps and Communications
Phil Leggetter
 
What's the ROI of Developer Relations?
Phil Leggetter
 
Real-Time Web Apps & Symfony. What are your options?
Phil Leggetter
 
Real-Time Web Apps in 2015 & Beyond
Phil Leggetter
 
Patterns and practices for building enterprise-scale HTML5 apps
Phil Leggetter
 
Fed London - January 2015
Phil Leggetter
 
How to Build Single Page HTML5 Apps that Scale
Phil Leggetter
 
Realtime Web Apps in 2014 & Beyond
Phil Leggetter
 
BladeRunnerJS Show & Tell
Phil Leggetter
 
Testing Ginormous JavaScript Apps - ScotlandJS 2014
Phil Leggetter
 
How to Build Front-End Web Apps that Scale - FutureJS
Phil Leggetter
 
Using BladeRunnerJS to Build Front-End Apps that Scale - Fluent 2014
Phil Leggetter
 
Ad

Recently uploaded (20)

PDF
Transcript: New from BookNet Canada for 2025: BNC BiblioShare - Tech Forum 2025
BookNet Canada
 
PDF
Transforming Utility Networks: Large-scale Data Migrations with FME
Safe Software
 
PDF
Using FME to Develop Self-Service CAD Applications for a Major UK Police Force
Safe Software
 
PDF
Empower Inclusion Through Accessible Java Applications
Ana-Maria Mihalceanu
 
PDF
Newgen 2022-Forrester Newgen TEI_13 05 2022-The-Total-Economic-Impact-Newgen-...
darshakparmar
 
PDF
DevBcn - Building 10x Organizations Using Modern Productivity Metrics
Justin Reock
 
PDF
How Startups Are Growing Faster with App Developers in Australia.pdf
India App Developer
 
PDF
Smart Trailers 2025 Update with History and Overview
Paul Menig
 
PPTX
WooCommerce Workshop: Bring Your Laptop
Laura Hartwig
 
PDF
POV_ Why Enterprises Need to Find Value in ZERO.pdf
darshakparmar
 
PDF
July Patch Tuesday
Ivanti
 
PDF
New from BookNet Canada for 2025: BNC BiblioShare - Tech Forum 2025
BookNet Canada
 
PDF
LOOPS in C Programming Language - Technology
RishabhDwivedi43
 
PDF
CIFDAQ Market Insights for July 7th 2025
CIFDAQ
 
PDF
What Makes Contify’s News API Stand Out: Key Features at a Glance
Contify
 
PPTX
From Sci-Fi to Reality: Exploring AI Evolution
Svetlana Meissner
 
PPTX
AUTOMATION AND ROBOTICS IN PHARMA INDUSTRY.pptx
sameeraaabegumm
 
PDF
CIFDAQ Token Spotlight for 9th July 2025
CIFDAQ
 
PPTX
AI Penetration Testing Essentials: A Cybersecurity Guide for 2025
defencerabbit Team
 
PDF
Agentic AI lifecycle for Enterprise Hyper-Automation
Debmalya Biswas
 
Transcript: New from BookNet Canada for 2025: BNC BiblioShare - Tech Forum 2025
BookNet Canada
 
Transforming Utility Networks: Large-scale Data Migrations with FME
Safe Software
 
Using FME to Develop Self-Service CAD Applications for a Major UK Police Force
Safe Software
 
Empower Inclusion Through Accessible Java Applications
Ana-Maria Mihalceanu
 
Newgen 2022-Forrester Newgen TEI_13 05 2022-The-Total-Economic-Impact-Newgen-...
darshakparmar
 
DevBcn - Building 10x Organizations Using Modern Productivity Metrics
Justin Reock
 
How Startups Are Growing Faster with App Developers in Australia.pdf
India App Developer
 
Smart Trailers 2025 Update with History and Overview
Paul Menig
 
WooCommerce Workshop: Bring Your Laptop
Laura Hartwig
 
POV_ Why Enterprises Need to Find Value in ZERO.pdf
darshakparmar
 
July Patch Tuesday
Ivanti
 
New from BookNet Canada for 2025: BNC BiblioShare - Tech Forum 2025
BookNet Canada
 
LOOPS in C Programming Language - Technology
RishabhDwivedi43
 
CIFDAQ Market Insights for July 7th 2025
CIFDAQ
 
What Makes Contify’s News API Stand Out: Key Features at a Glance
Contify
 
From Sci-Fi to Reality: Exploring AI Evolution
Svetlana Meissner
 
AUTOMATION AND ROBOTICS IN PHARMA INDUSTRY.pptx
sameeraaabegumm
 
CIFDAQ Token Spotlight for 9th July 2025
CIFDAQ
 
AI Penetration Testing Essentials: A Cybersecurity Guide for 2025
defencerabbit Team
 
Agentic AI lifecycle for Enterprise Hyper-Automation
Debmalya Biswas
 

Why you should be using Web Components. And How - DevWeek 2015

  • 2. Why you should be using Web Components Now. And How. PHIL @LEGGETTER Head of Evangelism 2 / 145
  • 3. What we'll cover What are Web Components? State of native support Componentised Web Apps now Why Web Components are the future! 3 / 145
  • 4. What we'll cover What are Web Components? State of native support Componentised Web Apps now Why Web Components are the future! ♥ Eric Bidelman's Google IO 2014 talk ♥ 4 / 145
  • 5. What are Web Components? 5 / 145
  • 6. What are Web Components? Custom Elements HTML Templates Shadow DOM HTML Imports 6 / 145
  • 8. <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
  • 9. <header>I'm a header</header> Stuff around sections or grouping content e.g. main, nav, footer, figure, article, asideetc. <progress /> Form improvements e.g. meter, datalist, keygen, output <video /> Embedded content e.g. audio, canvas, svg, math HTML5 Elements 9 / 145
  • 10. 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>&copy; me</footer> <script src="js/script.js"></script> </body> </html> 10 / 145
  • 14. The Solution: <Custom Elements /> More than just markup IMHO the most important part of Web Components 14 / 145
  • 15. 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
  • 16. 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
  • 17. 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
  • 18. 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
  • 19. 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
  • 20. <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/[email protected]" /> Start Simple - An Avatar 20 / 145
  • 21. Custom Elements <my-avatar service="twitter" username="leggetter" /> 21 / 145
  • 22. Custom Elements <my-avatar service="twitter" username="leggetter" /> <script> var MyAvatarPrototype = Object.create(HTMLElement.prototype); 22 / 145
  • 23. 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
  • 24. 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
  • 25. 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
  • 26. 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
  • 27. <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
  • 28. Custom Elements - Extending <img is="my-avatar-ext" service="twitter" username="leggetter" /> 28 / 145
  • 29. Custom Elements - Extending <img is="my-avatar-ext" service="twitter" username="leggetter" /> <script> var MyAvatarExtPrototype = Object.create(HTMLImageElement.prototype); 29 / 145
  • 30. 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
  • 31. 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 elements 31 / 145
  • 32. Custom Elements - Lifecycle createdCallback attachedCallback detachedCallback attributeChangedCallback(attrName, oldVal, newVal) 32 / 145
  • 33. Create Elements using JavaScript <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
  • 37. 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
  • 38. 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
  • 39. 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
  • 40. 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
  • 41. 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', { 41 / 145
  • 42. Why native HTML Templates? Libraries → Native Native benefits Document fragment = lightweight Inert until cloned/used 42 / 145
  • 43. Shadow DOM DOM/CSS "scoping" / protection 43 / 145
  • 44. Shadow DOM - Already using it 44 / 145
  • 45. Shadow DOM SubTrees Light DOM Shadow DOM Composed (rendered) DOM 45 / 145
  • 46. Light DOM <my-custom-element> <q>Hello World</q> <!-- part of my-custom-element's light DOM --> </my-custom-element> 46 / 145
  • 47. 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
  • 48. Rendered DOM <my-custom-element> <span>People say: <q>Hello World</q></span> <footer>sometimes</footer> </my-custom-element> 48 / 145
  • 49. Shadow DOM - Problems it solves 49 / 145
  • 50. 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
  • 51. 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
  • 52. Shadow DOM - In Action Create DevWeek 52 / 145
  • 53. 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
  • 54. 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
  • 55. 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
  • 56. 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
  • 57. 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', { 57 / 145
  • 58. Why Shadow DOM? DOM & CSS Scoping Protection for all: Page and Element Encapsulation 58 / 145
  • 59. HTML Imports Loading & Dependency Management 59 / 145
  • 60. 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
  • 61. 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
  • 62. HTML Imports - Composition team-pusher.html 62 / 145
  • 63. HTML Imports - Composition team-pusher.html <link rel="import" href="my-avatar-import.html" /> 63 / 145
  • 64. HTML Imports - Composition team-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
  • 65. HTML Imports - Composition team-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
  • 66. HTML Imports - Composition Demo <link rel="import" href="assets/team-pusher.html" /> <team-pusher></team-pusher> 66 / 145
  • 67. HTML Imports - Composition Demo <link rel="import" href="assets/team-pusher.html" /> <team-pusher></team-pusher> maxthelion twitter copypastaa twitter zimbatm twitter loicdumas twitter mdpye twitter olga_dukova twitter pawel_ledwon twitter hamchapman twitter rumblesan twitter jamessiddle twitter willsewell_ twitter leggetter twitter 67 / 145
  • 68. HTML Imports - Gotchas / Patterns! 68 / 145
  • 69. Get & use documentfrom the currentScript ( function( currentScript ) { var ownerDoc = currentScript.ownerDocument; } )( document._currentScript || document.currentScript ); 69 / 145
  • 70. importNodeand NOT cloneNodefor Template // Note: use ownerDoc var content = ownerDoc.querySelector( '#my-template' ); var clone = ownerDoc.importNode( content, true ); 70 / 145
  • 71. You can't <link>into the Shadow DOM <template> <link rel="stylesheet" href="path/to/style.css" /> </template> 71 / 145
  • 72. Why Use HTML Imports? Bundle JS/HTML/CSS → single URL Basic dependency management Sharing & reuse Supports composition 72 / 145
  • 73. State of Native Support 73 / 145
  • 74. Browsers Chrome 41 Firefox 36 Safari 8 IE 10 Custom Elements 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
  • 75. Firefox https://hacks.mozilla.org/2014/12/mozilla-and-web- “Mozilla will not ship an implementation of HTML Imports. We expect that once JavaScript modules ... is shipped, the way we look at this problem will have changed. 75 / 145
  • 77. 77 / 145 IE UserVoice
  • 79. All is not Lost 79 / 145
  • 80. Browsers - with Polyfills Chrome 42 Firefox 36 Safari 8 IE 10 Custom Elements 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 limitations 80 / 145
  • 82. Componentised Web Apps now - questions? Should native browser support stop us thinking about building componentised web apps? 82 / 145
  • 83. Componentised Web Apps now - questions? Should native browser support stop us thinking about building componentised web apps? No! 83 / 145
  • 84. Componentised Web Apps now - questions? Should native browser support stop us thinking about building componentised web apps? No! Should we be build componentised web apps anyway? 84 / 145
  • 85. Componentised Web Apps now - questions? Should native browser support stop us thinking about building componentised web apps? No! Should we be build componentised web apps anyway? We're already building web apps out of components right now! 85 / 145
  • 91. AngularJS <script src="js/angular.min.js"></script> <script> angular.module('demo', []) .directive('ngAvatar', function () { return { restrict:"AEC", scope: { service: '@', username: '@' }, 91 / 145
  • 92. 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
  • 93. 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
  • 94. 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
  • 99. 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
  • 100. 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
  • 101. 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
  • 102. 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> 102 / 145
  • 105. 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
  • 106. 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
  • 107. 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
  • 108. 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
  • 109. Many More... KnockoutJS Components Backbone components Backbone with React components CanJS components And... 109 / 145
  • 112. 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
  • 113. 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
  • 114. 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
  • 115. 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
  • 116. 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
  • 119. Who's Building Componentised Web Apps now? 119 / 145
  • 120. Who's Building Componentised Web Apps now? Angular, Ember, Backbone, Knockout, React, Web Components with Polyfills, Polymer You probably are already <ng-avatar service="twitter" username="leggetter" /> vs. <my-avatar service="twitter" username="leggetter" /> 120 / 145
  • 121. Examples From Eric's Slides GitHub Chrome OS GMail built in Polymer Topeka game built in Polymer 121 / 145
  • 122. Why Web Components are the future! 122 / 145
  • 123. 1. You're already building componentised web apps If you're not, you probably should be 123 / 145
  • 124. 2. Trends & Demand 124 / 145
  • 125. Libraries Alignment toward Web Components Angular - Directives Ember - Components Knockout - Components Polymer - build upon Web Components Angular 2... 125 / 145
  • 127. Browser Vendor Support Google Opera - uses Blink Mozilla Microsoft - ? previously: HTA & ASP.NET Controls In high demand on IE UserVoice Apple - ? 127 / 145
  • 128. 128 / 145 In Demand
  • 129. 3. Encourages good software development Component-based Development 129 / 145
  • 130. 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> 130 / 145
  • 131. Encapsulation Shadow DOM - Style & DOM encapsulation Does NOT offer JavaScript protection Hacky Custom Element leggetter twitter Don't click me! 131 / 145
  • 132. Loose Coupling Custom Events Element API (interface) Existing messaging frameworks 132 / 145
  • 133. 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
  • 134. 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
  • 135. 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
  • 138. High Cohesion myavatar.html ├── js/script.js ├── css/styles.css └── img/bg.png 138 / 145
  • 139. Problems? Solved in the future? HTML Imports Vulcanize | HTTP2 Shared scripts? Cache Multiple versions? Better Cross-component communication? Allow <link>for CSS in Shadow DOM? 139 / 145
  • 140. Summary Custom Elements - Awesome 140 / 145
  • 141. Summary Custom Elements - Awesome HTML Templates, Shadow DOM, HTML Imports - Native FTW 141 / 145
  • 142. Summary Custom Elements - Awesome HTML Templates, Shadow DOM, HTML Imports - Native FTW You can & are building componentised web apps now 142 / 145
  • 143. 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
  • 144. 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
  • 145. Why you should be using Web Components Now. And How. Questions? leggetter.github.io/web-components-now PHIL @LEGGETTER Head of Evangelism 145 / 145