On Github robdodson / webcomponents-cascade
Freelance front-end developer
I blog at...
We're going to cover a lot of material...
Be sure to enable the following in chrome://flags
Experimental Web Platform features
Experimental JavaScript
HTML Imports
What are web components...
The 4 underlying technologies that collectively make up web components
Tools that we can use to start experimenting...
<nav class="navbar navbar-default" role="navigation"> <!-- Brand and toggle get grouped for better mobile display --> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse"> <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 navbar-ex1-collapse"> <ul class="nav navbar-nav"> <li class="active"><a href="#">Link</a></li> <li><a href="#">Link</a></li> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown <b class="caret"></b></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><a href="#">Separated link</a></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">Dropdown <b class="caret"></b></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><a href="#">Separated link</a></li> </ul> </li> </ul> </div><!-- /.navbar-collapse --> </nav>
<nav class="navbar navbar-default" role="navigation"> <!-- Brand and toggle get grouped for better mobile display --> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse"> <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 navbar-ex1-collapse"> <ul class="nav navbar-nav"> <li class="active"><a href="#">Link</a></li> <li><a href="#">Link</a></li> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown <b class="caret"></b></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><a href="#">Separated link</a></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">Dropdown <b class="caret"></b></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><a href="#">Separated link</a></li> </ul> </li> </ul> </div><!-- /.navbar-collapse --> </nav>
<twbs-navbar> <a href="#">Home</a> <a href="#">About</a> <a href="#">Bacon</a> </twbs-navbar>
"Umbrella" term
Collectively, let us do cool stuff...
pie-chart tag...
You can see why this could be a big deal...
Web Components Revolution...
Imagine you're building a data viz library
There's actually 4 technologies that make up web components
And we're going to go through each of them
Templates: Just like mustache
Shadow DOM: Some things public and other things private
Custom Elements: The interface
<template> <p>Once upon a time...</p> <img src=""/> <!-- Fill me in when you're ready --> <script> alert("I'm chillin."); // Totally inert! </script> </template>
<template id="my-template"> <img src=""/> <p>Templates. Full of WIN!</p> <script> alert("I'm alive!") </script> </template> <script> var tmpl = document.querySelector("#my-template"); tmpl.content.querySelector("img").src = "corgi.gif"; document.body.appendChild(tmpl.content.cloneNode(true)); </script>Run
Templates. Full of WIN!
{{ }} tags don't do anything...yet
You'll have to append each separately
Take some HTML and CSS nodes, bundle them together
Styles are scoped to the node
HTML cannot be traversed by external javascript
Same tech as browser makers
<video src="./bunny.webm" controls></video>
Shadow Host
The node that contains all of our shadow DOM
Shadow Root
The first node in the shadow DOM
Shadow Boundary
The barrier that protects our shadow DOM
<div class="widget">Hello, world!</div> <script> var host = document.querySelector(".widget"); var root = host.createShadowRoot(); root.innerHTML = "<em>I'm inside yr div!</em>"; </script>Run
Only descendants of the Shadow Root will be rendered.
Inspect with dev tools<template> <style> h3 { color: white; background: tomato; } </style> <h3>A Shadow H3 Header</h3> </template>
<script> var tmpl = document.querySelector("template"); var host = document.querySelector(".widget"); var root = host.createShadowRoot(); root.appendChild(tmpl.content.cloneNode(true)); </script>
applyAuthorStyles (default: false)
false: Author styles will not affect your component.
true: Author styles will affect your component.
These are good for broad changes but you probably also want to be able to expose specific elements for the user to style
resetStyleInheritance (default: false)
false: Inheritable styles will affect your component.
true: Inheritable styles will not affect your component.
<template> <style> :host { border: 5px solid red; } </style> <h2>Oh hai!</h2> </template>
<template> <style> :host(.skinny) h2 { font-family: 'Open Sans Condensed'; letter-spacing: -0.09em; } </style> <h2>Oh hai!</h2> </template>
<template> <style> h2 { font-family: var(header-font); } </style> <h2>Custom prop stylin'</h2> </template>
<style> body { var-header-font: Courier; } </style>
<template> <h2>A Wild <content></content> Appeared!</h2> </template>
<div class="widget">Jigglypuff</div>
<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>
<template> <h2>First Shadow DOM</h2> <content></content> </template>
<style> /* the hat */ .widget ^ h2 { color: red; } /* the cat */ .widget ^^ h2 { font-family: Courier; } </style>
document.register('tag-name', { prototype: proto })
var tmpl = document.querySelector("#some-template"); var WidgetProto = Object.create(HTMLElement.prototype); WidgetProto.createdCallback = function() { var root = this.createShadowRoot(); root.appendChild(tmpl.content.cloneNode(true)); }; var Widget = document.register("my-widget", { prototype: WidgetProto });
<my-widget></my-widget> // OR
document.createElement("my-widget") // OR
new Widget()
createdCallback() When a new instance is created. Use like a constructor
enteredViewCallback()* When an element is added to the page*previously named enteredDocumentCallback
leftViewCallback()* When an element is removed from the page*previously named leftDocumentCallback
attributeChangedCallback(attrName, oldVal, newVal) When one of an element's attributes changes
var MyButton = document.register("my-button", { extends: "button", prototype: Object.create(HTMLButtonElement.prototype) });
<button is="my-button"></button> var btn = document.createElement("button", "my-button"); var btn = new MyButton();Called "Type Extension Elements"
<!-- Do not use! Will be removed. --> <element name="my-widget"> ... </element>
<mc-signup url="http://robdodson.us7.list-manage.com/subscribe/post" u="5727aa0eb1ccbf4ae68284189" id="6719a28b56"> </mc-signup>
<chart-pie values="[...]"></chart-pie> <chart-doughnut values="[...]"></chart-doughnut> <chart-polar-area values="[...]"></chart-polar-area> <chart-radar values="[...]"></chart-radar> <chart-line values="[...]"></chart-line> <chart-bar values="[...]"></chart-bar>
<moment-now></moment-now>
<moment-format tokens="MMMM Do YYYY, h:mm:ss a"> <moment-now></moment-now> </moment-format>
<moment-format tokens="dddd, MMMM Do YYYY"> <moment-parse>Oct 26, 1985</moment-parse> </moment-format>
<moment-from> <moment-parse>Oct 26, 1985</moment-parse> <moment-now></moment-now> </moment-from>
Scaffolding
Encapsulation
Extensions
Packaging
<link rel="import" href="my-import.html">
<head> <link rel="import" href="./imports/chart.html"> </head> <body> <chart-pie></chart-pie> <!-- ready to rock --> </body>
No need to check if the import is loaded
* Behind a flag
In chrome://flags
Experimental Web Platform features
Experimental JavaScript
HTML Imports
Show Shadow DOM in Developer Tools
<script src="polymer.js"></script>
<polymer-element name="my-element"> <template> <h2> Hello, I'm a Polymer element </h2> </template> <script>Polymer("my-element");</script> </polymer-element>
<!-- Ready to rock! --> <my-element></my-element>
<polymer-element name="fav-color" attributes="color"> <template> <h2>My favorite color is: {{ color }}</h2> </template> <script> Polymer("fav-color", { color: "Orange" }); </script> </polymer-element>
<fav-color color="Purple"></fav-color>
<polymer-element name="color-picker" attributes="color"> <template> <p>My favorite color is <span style="background: {{ color }}">{{ color }}</span> </p> <input type="text" value="{{ color }}"> </template> <script> Polymer("color-picker", { color: "Tomato" }); </script> </polymer-element>
created() When a new instance is created. Use like a constructor
enteredView() When an element is added to the page
leftView() When an element is removed from the page
attributeChanged(attrName, oldVal, newVal) When one of an element's attributes changes