web Components
![](https://s3.amazonaws.com/media-p.slid.es/uploads/248920/images/1807123/pasted-from-clipboard.png)
- frontend bucharest meetup -
Because why the f*#k not?
(we've used worst things before)
Andrei Antal
KALON
Contents
- web components - the what and the how
- Polymer 1.0 (now at 1.2) - sweet sugar candy components
- conclusion and discussions
WEB COMPONENTS
- Set of standards that allow us to bundle markup, styles and scripts into custom HTML elements
- Slowly gaining support on browsers - but for now we need a polyfill ( http://webcomponents.org/ - 117KB minified)
- Google as the main driving force
- We've been using them but now ca can also create our own
WEB COMPONENTS
<input type="text" value="Please insert a value...">
![](https://s3.amazonaws.com/media-p.slid.es/uploads/248920/images/1955378/Screen_Shot_2015-11-16_at_23.06.21.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/248920/images/1955394/Screen_Shot_2015-11-16_at_23.09.35.png)
<input type="range">
WEB COMPONENTS
![](https://s3.amazonaws.com/media-p.slid.es/uploads/248920/images/1958633/Screen_Shot_2015-11-17_at_17.05.26.png)
WEB COMPONENTS
![](https://s3.amazonaws.com/media-p.slid.es/uploads/248920/images/1954981/video-shadow-dom_21.jpg)
WEB COMPONENTS
The sin...
![](https://s3.amazonaws.com/media-p.slid.es/uploads/248920/images/1957706/blog-image1.png)
WEB COMPONENTS
![](https://s3.amazonaws.com/media-p.slid.es/uploads/248920/images/1957777/steve-shadow-dom.jpg)
Scumbag browser vendors
WEB COMPONENTS
![](https://s3.amazonaws.com/media-p.slid.es/uploads/248920/images/1954928/enable-shadow-dom.png)
Enabling the DOM they don't want you to see
WEB COMPONENTS
Core elements of web components:
- HTML Templates
- Custom elements
- Shadow DOM
- HTML Imports
WEB COMPONENTS - HTML TEMPLATES
Hiding HTML elements
<div style="display:none">
<p>My image here: </p>
<img src="image.jpeg">
</div>
<script type="text/template" id="template">
<div>
<h1>Web Components</h1>
<img src="image.jpeg">
</div>
</script>
Template scripts
WEB COMPONENTS - HTML TEMPLATES
Defining HTML templates
<template id="template">
<style>
...
</style>
<div>
<h1>Web Components</h1>
<img src="image.jpeg">
</div>
</template>
WEB COMPONENTS - HTML TEMPLATES
Initialising the template
<div id="host"></div>
<script>
var template = document.querySelector('#template');
var clone = document.importNode(template.content, true);
var host = document.querySelector('#host');
host.appendChild(clone);
</script>
<div id="host"></div>
<script>
var template = document.querySelector('#template');
var clone = document.importNode(template.content, true);
var host = document.querySelector('#host');
host.appendChild(clone);
</script>
<div id="host"></div>
<script>
var template = document.querySelector('#template');
var clone = document.importNode(template.content, true);
var host = document.querySelector('#host');
host.appendChild(clone);
</script>
<div id="host"></div>
<script>
var template = document.querySelector('#template');
var clone = document.importNode(template.content, true);
var host = document.querySelector('#host');
host.appendChild(clone);
</script>
WEB COMPONENTS - HTML TEMPLATES
if(!window.HTMLTemplateElement) {
....
}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/248920/images/1955738/Screen_Shot_2015-11-17_at_00.19.39.png)
Feature detection
WEB COMPONENTS - CUSTOM ELEMENTS
Build and use custom tags
![](https://s3.amazonaws.com/media-p.slid.es/uploads/248920/images/1957800/tumblr_n3vmhcoWSh1rvsbh9o1_500.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/248920/images/1957830/programming_o_1075452.jpg)
WEB COMPONENTS - CUSTOM ELEMENTS
Building a custom element
var XComponent = document.registerElement('x-component');
<x-component></x-component>
var XComponent = document.registerElement('x-component');
var dom = new XComponent();
document.body.appendChild(dom);
document.registerElement('x-component');
var dom = document.createElement('x-component');
document.body.appendChild(dom);
WEB COMPONENTS - CUSTOM ELEMENTS
Extending a custom element
var proto = Object.create(HTMLElement.prototype);
proto.name = 'Custom Element';
proto.alert = function() {
alert('This is ' + this.name);
};
document.registerElement(
'x-component', {
prototype: proto
});
![](https://s3.amazonaws.com/media-p.slid.es/uploads/248920/images/1955868/customelements-inherit.png)
var proto = Object.create(HTMLElement.prototype);
proto.name = 'Custom Element';
proto.alert = function() {
alert('This is ' + this.name);
};
document.registerElement(
'x-component', {
prototype: proto
});
var proto = Object.create(HTMLElement.prototype);
proto.name = 'Custom Element';
proto.alert = function() {
alert('This is ' + this.name);
};
document.registerElement(
'x-component', {
prototype: proto
});
WEB COMPONENTS - CUSTOM ELEMENTS
Extending an existing element
var XComponent = document.registerElement('x-component', {
extends: 'input',
prototype: Object.create(HTMLInputElement.prototype)
});
<input is="x-component">
![](https://s3.amazonaws.com/media-p.slid.es/uploads/248920/images/1955890/customelements-relativetime.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/248920/images/1955891/customelements-time.png)
Custom element example: github
WEB COMPONENTS - CUSTOM ELEMENTS
Lifecycle hooks
Callback name | Called when |
createdCallback | an instance of the element is created |
attachedCallback | an instance was inserted into the document |
detachedCallback | an instance was removed from the document |
attributeChangedCallback(attrName, oldVal, newVal) | an attribute was added, removed, or updated |
WEB COMPONENTS - CUSTOM ELEMENTS
Feature detection
if(!document.registerElement) {
....
}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/248920/images/1955930/Screen_Shot_2015-11-17_at_00.51.46.png)
WEB COMPONENTS - SHADOW DOM
![](https://s3.amazonaws.com/media-p.slid.es/uploads/248920/images/1957785/6bac7cc9fea7563ce267904a33850c6d8cf79c1d8d8269901a8f46dc1737c59c.jpg)
WEB COMPONENTS - SHADOW DOM
![](https://s3.amazonaws.com/media-p.slid.es/uploads/248920/images/1955967/shadowdom-devtools.png)
WEB COMPONENTS - SHADOW DOM
<div id="host"></div>
<script>
var host = document.querySelector('#host');
var root = host.createShadowRoot();
var div = document.createElement('div');
div.textContent = 'This is Shadow DOM';
root.appendChild(div);
</script>
Building the Shadow DOM
![](https://s3.amazonaws.com/media-p.slid.es/uploads/248920/images/1957190/Screen_Shot_2015-11-17_at_09.21.38.png)
<div id="host"></div>
<script>
var host = document.querySelector('#host');
var root = host.createShadowRoot();
var div = document.createElement('div');
div.textContent = 'This is Shadow DOM';
root.appendChild(div);
</script>
<div id="host"></div>
<script>
var host = document.querySelector('#host');
var root = host.createShadowRoot();
var div = document.createElement('div');
div.textContent = 'This is Shadow DOM';
root.appendChild(div);
</script>
WEB COMPONENTS - SHADOW DOM
Structure of shadow DOM
document
element (shadow host)
shadow root
contents
WEB COMPONENTS - SHADOW DOM
<div id="host">Light DOM</div>
<script>
var container = document.querySelector('#host');
var root1 = container.createShadowRoot();
var root2 = container.createShadowRoot();
root1.innerHTML = '<div>Root 1 FTW</div>';
root2.innerHTML = '<div>Root 2 FTW</div>';
</script>
Multiple shadow roots
![](https://s3.amazonaws.com/media-p.slid.es/uploads/248920/images/1957209/Screen_Shot_2015-11-17_at_09.25.12.png)
WEB COMPONENTS - SHADOW DOM
<h1>Outer html</h1>
<div id="host">this content will be replaced</div>
<template id="hostTemplate">
<style>
h1 {
border: 1px solid black;
font-size:16px;
color:blue;
}
:host {
color:red;
}
</style>
<div class="outer">
<h1> Host HTML </h1>
styled from the shadow DOM
</div>
</template>
Using templates
WEB COMPONENTS - SHADOW DOM
<script>
var shadow = document.querySelector("#host").createShadowRoot();
var template = document.querySelector("#hostTemplate");
var clone = document.importNode(template.content, true);
shadow.appendChild(clone);
</script>
Using templates
![](https://s3.amazonaws.com/media-p.slid.es/uploads/248920/images/1957243/Screen_Shot_2015-11-17_at_09.40.30.png)
<script>
var shadow = document.querySelector("#host").createShadowRoot();
var template = document.querySelector("#hostTemplate");
var clone = document.importNode(template.content, true);
shadow.appendChild(clone);
</script>
<script>
var shadow = document.querySelector("#host").createShadowRoot();
var template = document.querySelector("#hostTemplate");
var clone = document.importNode(template.content, true);
shadow.appendChild(clone);
</script>
<h1>Outer html</h1>
<div id="host">this content will be replaced</div>
WEB COMPONENTS - SHADOW DOM
Using templates
![](https://s3.amazonaws.com/media-p.slid.es/uploads/248920/images/1957834/efba715fd24ea336d268be73323f03f84faaf26b1dcaf6bbc117112711bf3db4.jpg)
WEB COMPONENTS - SHADOW DOM
<div id="host">
<h1>This is Shadow DOM</h1>
</div>
Adding host element content
![](https://s3.amazonaws.com/media-p.slid.es/uploads/248920/images/1957256/Screen_Shot_2015-11-17_at_09.46.08.png)
<template id="hostTemplate">
<style>
...
</style>
<div id="container">
<span> Something something dark side </span>
<content select="h1"></content> // Insert h1 here
</div>
</template>
WEB COMPONENTS - SHADOW DOM
<div id="host">
<h2>Andrei</h2>
<h2>Antal</h2>
<div id="title">Frontend developer</div>
<h4 class="f">wannabe</h4>
</div>
<template id="hostTemplate">
<header>
<content select="h2"></content>
</header>
<section>
<content select="#title"></content>
</section>
<footer>
<content select=".f"></content>
</footer>
</template>
Multiple insertion points
WEB COMPONENTS - SHADOW DOM
![](https://s3.amazonaws.com/media-p.slid.es/uploads/248920/images/1957295/Screen_Shot_2015-11-17_at_10.06.10.png)
WEB COMPONENTS - SHADOW DOM
if(!document.body.createShadowRoot) {
....
}
Feature detection
![](https://s3.amazonaws.com/media-p.slid.es/uploads/248920/images/1957230/Screen_Shot_2015-11-17_at_01.16.08.png)
WEB COMPONENTS - HTML IMPORTS
<!-- warning.html -->
<div class="warning">
<style scoped>
h3 {
color: red;
}
</style>
<h3>Warning!</h3>
<p>This page is under construction</p>
</div>
<div class="outdated">
<h3>Heads up!</h3>
<p>This content may be out of date</p>
</div>
Importing HTML documents into other HTML documents
WEB COMPONENTS - HTML IMPORTS
<head>
<link rel="import" href="warnings.html">
</head>
Importing HTML documents into other HTML documents
// Grab DOM from warning.html's document.
var el = content.querySelector('.warning');
<body>
...
<script>
var link = document.querySelector('link[rel="import"]');
var content = link.import;
document.body.appendChild(el.cloneNode(true));
</script>
</body>
WEB COMPONENTS - HTML IMPORTS
<head>
<link rel="import" href="bootstrap.html">
</head>
A practical example - including Bootstrap
<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>
...
<!-- scaffolding markup -->
<template>
...
</template>
WEB COMPONENTS - HTML IMPORTS
<!-- import.html -->
<template>
<h1>Hello World!</h1>
<!-- Img is not requested until the <template> goes live. -->
<img src="world.png">
<script>alert("Executed when the template is activated.");</script>
</template>
<head>
<link rel="import" href="import.html">
</head>
<body>
<div id="container"></div>
<script>
var link = document.querySelector('link[rel="import"]');
// Clone the <template> in the import.
var template = link.import.querySelector('template');
var clone = document.importNode(template.content, true);
document.querySelector("#container").appendChild(clone);
</script>
</body>
Including templates
<!-- import.html -->
<template>
<h1>Hello World!</h1>
<!-- Img is not requested until the <template> goes live. -->
<img src="world.png">
<script>alert("Executed when the template is activated.");</script>
</template>
<head>
<link rel="import" href="import.html">
</head>
<body>
<div id="container"></div>
<script>
var link = document.querySelector('link[rel="import"]');
// Clone the <template> in the import.
var template = link.import.querySelector('template');
var clone = document.importNode(template.content, true);
document.querySelector("#container").appendChild(clone);
</script>
</body>
<!-- import.html -->
<template>
<h1>Hello World!</h1>
<!-- Img is not requested until the <template> goes live. -->
<img src="world.png">
<script>alert("Executed when the template is activated.");</script>
</template>
<head>
<link rel="import" href="import.html">
</head>
<body>
<div id="container"></div>
<script>
var link = document.querySelector('link[rel="import"]');
// Clone the <template> in the import.
var template = link.import.querySelector('template');
var clone = document.importNode(template.content, true);
document.querySelector("#container").appendChild(clone);
</script>
</body>
WEB COMPONENTS - HTML IMPORTS
if(!"import" in document.createElement("link")) {
....
}
Feature detection
![](https://s3.amazonaws.com/media-p.slid.es/uploads/248920/images/1956048/Screen_Shot_2015-11-17_at_01.18.43.png)
WEB COMPONENTS
Browser compatibility
POLYMER
- WEB COMPONENTS MADE SWEET -
- build on top of web components - by GOOGLE
- offers syntactic sugar for fast creation of reusable components
- adds a few tricks to components (eg. data binding)
- EXTENSIVE POLYMER ELEMENT CATALOG
![](https://s3.amazonaws.com/media-p.slid.es/uploads/248920/images/1810486/Polymer-logo-featured.png)
POLYMER - element starter
<link rel="import" href="../bower_components/polymer/polymer.html">
.....
<link rel="import" href="comp/hello-folks.html">
</head>
<body>
<hello-folks></hello-folks>
....
<script>
Polymer({
is: "hello-folks",
})
</script>
<dom-module id="hello-folks">
<style>
h1 { color: red; }
h3 { color: blue; }
</style>
<template>
<h1>Hello</h1>
<h3>folks</h3>
</template>
</dom-module>
POLYMER - CUSTOM Properties
<script>
Polymer({
is: "hello-folks",
properties : {
aSimpleProp : String,
aComputedProp : {
type: String,
computed: "comp(aSimpleProp)"
}
},
comp(prop){
return "computed: " + prop
}
});
</script>
.....
<hello-folks a-simple-prop="a prop"></hello-folks>
....
<dom-module id="hello-folks">
<style>
h1 { color: red; }
h3 { color: blue; }
</style>
<template>
<h1>Hello</h1>
<h3>folks <span>{{aComputedProp}}</span></h3>
</template>
</dom-module>
POLYMER - DATA BINDING
<dom-module id="hello-folks">
<style>
h1 { color: red; }
h3 { color: blue; }
</style>
<template>
Name: <input value={{nameValue::input}}>
<h1>Hello</h1>
<h3> <span>[[nameValue]]</span></h3>
<img src$="https://www.example.com/profiles/{{userId}}.jpg">
</template>
</dom-module>
- [[ property]] - one way data binding
- {{ property }} - automatic (one/two way) data binding
POLYMER - DATA BINDING
<!-- Property binding -->
<my-element selected="{{value}}"></my-element>
<!-- results in <my-element>.selected = this.value; -->
<!-- Attribute binding -->
<my-element selected$="{{value}}"></my-element>
<!-- results in <my-element>.setAttribute('selected', this.value); -->
<!-- class -->
<div class$="{{foo}}"></div>
<!-- style -->
<div style$="{{background}}"></div>
<!-- href -->
<a href$="{{url}}">
<!-- label for -->
<label for$="{{bar}}"></label>
<!-- dataset -->
<div data-bar$="{{baz}}"></div>
Binding properties and attributes
Native elements binding
POLYMER - Iterating over data
<script>
Polymer({
is: "hello-folks",
ready: function(){
this.things = [
{name: "a thing"},
{name: "some other thing"},
{name: "the last thing"}]
}
});
</script>
.......
<dom-module id="things-list">
<template>
<h1>A list of things</h1>
<ul>
<template is="dom-repeat"
items={{things}}
as="thing"
index-as="thing_no">
<li>thing <i>[[thing_no+1]]</i> - <b>[[thing.name]]</b></li>
</template>
</ul>
</template>
........
.......
<dom-module id="things-list">
<template>
<h1>A list of things</h1>
<ul>
<template is="dom-repeat"
items={{things}}
as="thing"
index-as="thing_no">
<li>thing <i>[[thing_no+1]]</i> - <b>[[thing.name]]</b></li>
</template>
</ul>
</template>
........
.......
<dom-module id="things-list">
<template>
<h1>A list of things</h1>
<ul>
<template is="dom-repeat"
items={{things}}
as="thing"
index-as="thing_no">
<li>thing <i>[[thing_no+1]]</i> - <b>[[thing.name]]</b></li>
</template>
</ul>
</template>
........
.......
<dom-module id="things-list">
<template>
<h1>A list of things</h1>
<ul>
<template is="dom-repeat"
items={{things}}
as="thing"
index-as="thing_no">
<li>thing <i>[[thing_no+1]]</i> - <b>[[thing.name]]</b></li>
</template>
</ul>
</template>
........
.......
<dom-module id="things-list">
<template>
<h1>A list of things</h1>
<ul>
<template is="dom-repeat"
items={{things}}
as="thing"
index-as="thing_no">
<li>thing <i>[[thing_no+1]]</i> - <b>[[thing.name]]</b></li>
</template>
</ul>
</template>
........
.......
<dom-module id="things-list">
<template>
<h1>A list of things</h1>
<ul>
<template is="dom-repeat"
items={{things}}
as="thing"
index-as="thing_no">
<li>thing <i>[[thing_no+1]]</i> - <b>[[thing.name]]</b></li>
</template>
</ul>
</template>
........
POLYMER - ELEMENT COMPOSITION
<link rel="import" href="bower_components/polymer/polymer.html">
<link rel="import" href="components/entry-detail.html">
<dom-module id="entry-list">
<style>
</style>
<template>
<h1>ELEMENT LIST</h1>
<template is="dom-repeat" items="{{entries}}">
<h3> Element #<span>[[index]]</span></h3>
<entry-detail id="{{item.id}}">
</template>
</template>
</dom-module>
<script>
Polymer({
is: "hello-folks",
ready: function(){
this.entries = [...]
}
})
</script>
POLYMER - Routing
other options
CONCLUSION
- HTML sucks...
- CSS sucks...
![](https://s3.amazonaws.com/media-p.slid.es/uploads/248920/images/1958478/myboy.jpg)
CONCLUSION
![](https://s3.amazonaws.com/media-p.slid.es/uploads/248920/images/1958473/react-hype.jpg)
CONCLUSION
![](https://s3.amazonaws.com/media-p.slid.es/uploads/248920/images/1958789/58889867.jpg)
THANKS
![](https://s3.amazonaws.com/media-p.slid.es/uploads/248920/images/1958518/8ecb843accb81caa2d83a156ac03426ed936d5292c73aa0ae5068b03c13ddaaa.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/248920/images/1958528/58804416.jpg)
Reach me @
fb/antal.a.andrei
@andrei_antal
antal.andrei@icloud.com
Frontend Meetup - Web components
By Andrei Antal
Frontend Meetup - Web components
- 1,492