<nav class="navbar navbar-default">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Brand</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li class="active"><a href="#">Link <span class="sr-only">(current)</span></a></li>
<li><a href="#">Link</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">Separated link</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">One more separated link</a></li>
</ul>
</li>
</ul>
<form class="navbar-form navbar-left" role="search">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
<ul class="nav navbar-nav navbar-right">
<li><a href="#">Link</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">Separated link</a></li>
</ul>
</li>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
<nav class="navbar navbar-default">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Brand</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li class="active"><a href="#">Link <span class="sr-only">(current)</span></a></li>
<li><a href="#">Link</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">Separated link</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">One more separated link</a></li>
</ul>
</li>
</ul>
<form class="navbar-form navbar-left" role="search">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
<ul class="nav navbar-nav navbar-right">
<li><a href="#">Link</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">Separated link</a></li>
</ul>
</li>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
<twbs-navbar>
<a href="#">Home</a>
<a href="#">About</a>
<a href="#">Bacon</a>
</twbs-navbar>
1. Create your own HTML element
2. Scope your styles
3. React with lifecycle callbacks
Components for the web have been around for a long time, the idea of being able to write reusable and portable code is nothing new.
A web component is a standardised way of creating encapsulated, reusable user interface elements for the web.
built on nothing more than web standards, this has some huge benefits
interoperability
lifespan
portability
Eric Bidelman
based on a standart set of W3C Especification
Custom Elements
Shadow DOM
HTML Templates
HTML Imports
Custom Elements
Familiar with this kind of code?
<div class="abFed">
<div class="gTef">
<div class="ErkF">
<div class="qcSx">
<div class="iYrh">
</div>
</div>
</div>
</div>
</div>
Should not we expect more from the Web Platform?
with no soup anymore
<chat-app org="acme">
<room-list show-lobby="true" />
<message-list view="detailed">
<message user="..." text="..." />
<message user="..." text="..." />
<message user="..." text="..." />
<message user="..." text="..." />
</message-list>
</chat-app>
Sounds better, no?
Especification to use new types of DOM elements in a document and extend
There are already custom elements shipped with HTML5
<video> <figure> <article> <header> <aside> <nav> ...
one element, one feature / role / function
Do you think that will be created?
[ Tag name must have a dash ]
<!-- find a name to your element -->
<my-element></my-element>
Is it right? is it solved?
[ pseudo-class :unresolved ]
<style>
my-element { color: red; }
:unresolved { border: 1px solid black; }
</style>
Create with custom prototypes
[ Proto must inherit from HTMLElement ]
var proto = Object.create(
HTMLElement.prototype
);
var MyEl = document.registerElement(
'my-element', { prototype: proto }
);
Using JavaScript
<my-element></my-element> // OR
document.createElement('my-element'); // OR
new MyEl();
This type of element is called a Type Extension Custom Element. It lets you extend native elements APIs with your own properties
document.registerElement('x-super-input', {
extends: 'input',
prototype: Object.create(
HTMLInputElement.prototype, {
// custom prototype properties
})
});
Supported callback functions for custom elements are called lifecycle callbacks
var proto = Object.create(
HTMLElement.prototype
);
proto.createdCallback = function () {
this.innerHTML =
(this.getAttribute('name') || '?');
}
document.registerElement(
'my-header', {prototype: proto}
);
Shadow DOM
In example, let's consider the audio tag
<audio src="http://developer.mozilla.org/@api/deki/files/2926/=AudioTest_(1).ogg"
autoplay="autoplay" controls="controls">
Your browser does not support the <code>audio</code> element.
</audio>
Inspecting the element with Shadow Dom activated from dev tools, we can see the HTML markup for all rendered elements, inside the audio's shadow root.
The node that contains all of our shadow DOM
The first node in the shadow DOM
The barrier that protects our shadow DOM
<audio>
#document-
fragement
<div>
<div>
[ createShadowRoot is deprecated but, new API hasn't been standardized and none of the browsers currently support it ]
<div id="host"></div>
<script>
var host = document.querySelector('#host');
var root = host.createShadowRoot();
// host.attachShadow({mode: 'open'});
</script>
Encapsulation semi-permeably blocks content access and style leakage
var span = document.createElement('span');
span.textContent = 'This is Shadow DOM';
root.appendChild(span);
Encapsulation of shadowed content access and style targeting can be breached if a developer explicitly chooses to do so
var span = host.querySelector('span');
// this query will return NULL
div span { color: red; }
/* this style will not be applied */
var span = host.querySelector('::shadow span');
// this query will return the shadowed span
To show the shadow hosts' content in the presentation layer you have to add the <content> tag.
<content> will cherry pick all content from the shadow host and append it inside the shadow root.
<div class="widget">
Hello, world!
<p>This also gets rendered</p>
</div>
<script>
var host = document.querySelector('.widget');
var root = host.createShadowRoot();
root.textContent += 'Im inside yr div!';
root.textContent += '<content></content>';
</script>
Specific content can be taged with the select attribute.
<h3>Last Name:
<content select=".last-name"></content>
</h3>
<h3>First Name:
<content select=".first-name"></content>
</h3>
<h3>
<content select=""></content>
</h3>
<div class="widget">
Hello World
<span class="first-name">Rob</span>
<span class="last-name">Dodson</span>
</div>
The editor's draft of CSS Scoping specification defines the selectors which are related to Shadow DOM. Specifically, it defines the following selectors related to Shadow DOM:
HTML Templates
HTML Templates
<template> allows to declare HTML fragments
which means
Create your own, reusable templates using HTML markup
<template id="tmpl">
<style>
...
</style>
<div>
<h1>Web Components</h1>
<img src="image.jpeg">
</div>
</template>
Generate copies of your templates
var tmpl = document.querySelector('#tmpl');
var clone = tmpl.content.cloneNode(true);
document.body.appendChild(clone);
// var clone = document.importNode(
tmpl.content, true
);
This example uses cloneNode() on the template's contents; it could equivalently have used document.importNode(), which does the same thing. The only difference between these two APIs is when the node document is updated: with cloneNode() it is updated when the nodes are appended with appendChild(), with document.importNode() it is updated when the nodes are cloned.
HTML Imports
Fetches included scripts, styles, and templates
<head>
<link rel="import" href="/path/import.html">
</head>
Act on import documents when they arrive, or fail due to an error
<script async>
function importLoad(event) { }
function importError(event) { }
</script>
<link rel="import"
href="file.html"
onload="importLoad(event)"
onerror="importError(event)" />
Utilize the imported resources when they arrive by accessing the import document attached to the link element
<script async>
function importLoad(event) {
var doc = event.target.import;
var tmpl = doc.querySelector('#widget');
// do something with assets
}
</script>
Access the importing parent document or the imported sub-document via script from within the import sub-document
<!-- assume this is inside imported doc -->
<script>
var doc =
document.currentScript.ownerDocument;
// hook to the imported sub-doc
var mainDoc = doc;
// the 'doc' variable is a reference
// to the parent document that is
// bringing in the import
</script>
<head>
<link rel="import" href="warnings.html">
</head>
<body>
<script>
var link = document.querySelector('link[rel="import"]');
var content = link.import;
// Grab DOM from warning.html's document.
var el = content.querySelector('.warning');
document.body.appendChild(el.cloneNode(true));
</script>
</body>
// component.html
<template id="template">
<style>
h1 { color: orange; }
</style>
<div>
<h1>Web Components</h1>
<img src="http://webcomponents.org/img/logo.png">
</div>
</template>
<script>
var template = document.getElementById("template");
</script>
<script src="js/component.js"></script>
An example with all the pieces together
// component.js
var myComponent = document.registerElement('my-component', {
prototype: Object.create(HTMLElement.prototype, {
createdCallback: {
value: function() {
var templateClone = template.content.cloneNode(true);
var root = this.createShadowRoot()
root.appendChild(templateClone);
}
}
});
});
// index.html
...
<link rel="import" href="component.html">
</head>
<body>
<my-component>
<h1>This is a Custom Element</h1>
</my-component>
...
Native browser support is landing, but we'll still need polyfills for the foreseeable future
Piece of code (or plugin) that provides the technology that you, the developer, expect the browser to provide natively. Flattening the API landscape if you will.
There is a list of polyfills ranging from SVG to Android, Canvas for IE (using Silverlight as a bridge) to support <video>, <audio>, etc.
HTML5 Cross browser Polyfills
webcomponents.js is a set of polyfills built on top of the Web Components specifications. It makes it possible for developers to use these standards today across all modern browsers.
Polymer is a new type of library for the web, built on top of Web Components, and designed to leverage the evolving web platform on modern browsers.
X-Tag is a small JavaScript library, created and supported by Mozilla, that brings Web Components Custom Element capabilities to all modern browsers.
Bosonic is a set of tools that enable you to build Web Components as the spec currently describes, and supporting not-so-modern browsers like IE9.
SkateJS is a superset of the web component specs, with a very small footprint, that enables you to write performant web components using a functional rendering pipeline.
Description: Build a tag that displays top and bottom text over a meme picture
Requirements:
Extra Credit:
Description: Build a tag that arranges image elements in an organically uneven stack appearance
Requirements:
Extra Credit:
Description: Create a comment feed element that supports custom child elements for comments/replies
Requirements:
Extra Credit: