Notes / links ➹
google-map { display: block; height: 600px;}
<google-map latitude="37.77493" longitude="-122.41942"></google-map>
<template is="auto-binding"> <google-map-directions map="{{map}}" startAddress="San Francisco" endAddress="Mountain View" travelMode="TRANSIT"></google-map-directions> <google-map map="{{map}}" latitude="37.779" longitude="-122.3892"></google-map> </template>➹
<link rel="import" href="google-map-directions.html">
<google-map latitude="37.779" longitude="-122.3892" minZoom="9" maxZoom="11" fit> <google-map-marker latitude="37.779" longitude="-122.3892" title="Go Giants!" draggable="true"> <img src="http://www. ... SFGiants.png" /> </google-map-marker> </google-map> <google-map-directions startAddress="San Francisco" endAddress="Mountain View"></google-map-directions>
Notes / links ➹
<name-tag>
<p class="role">Speaker</p>
<p class="name">Estelle Weyl</p>
</name-tag>
<div class="name-tag"> <div class="outer"> <div class="logo">JSSummit<span>2015</span></div> <div class="tagline">Online JavaScript Conference</div> <div class="inner"> <div class="name">Estelle Weyl</div> <div class="role">Speaker</div> </div> <div class="footer">Follow us on Twitter @JSSummit<br> Follow the conversation using #JSSummit </div> </div> </div>➹
<div class="name-tag"> <div class="outer"> <header>JSSummit<span>2015</span> <p>Online JavaScript Conference</p> </header> <main> <p class="name">Estelle Weyl</p> <p class="role">Speaker</p> </main> <footer>Follow us on Twitter @JSSummit<br> Follow the conversation using #JSSummit </footer> </div> </div>
<div class="name-tag"> <div class="outer"> <div class="logo">JSSummit<span>2015</span></div> <div class="tagline">Online JavaScript Conference</div> <div class="inner"> <div class="name">Estelle Weyl</div> <div class="role">Speaker</div> </div> <div class="footer">Follow us on Twitter @JSSummit<br> Follow the conversation using #JSSummit </div> </div> </div> <div class="name-tag"> <div class="outer"> <div class="logo">JSSummit<span>2015</span></div> <div class="tagline">Online JavaScript Conference</div> <div class="inner"> <div class="name">Niniane Wang</div> <div class="role">CTO, Minted</div> </div> <div class="footer">Follow us on Twitter @JSSummit<br> Follow the conversation using #JSSummit </div> </div> </div> <div class="name-tag"> <div class="outer"> <div class="logo">JSSummit<span>2015</span></div> <div class="tagline">Online JavaScript Conference</div> <div class="inner"> <div class="name">Tim Berners-Lee</div> <div class="role">Web Developer</div> </div> <div class="footer">Follow us on Twitter @JSSummit<br> Follow the conversation using #JSSummit </div> </div> </div>➹
var data = [ {name: 'Estelle Weyl', role: 'Speaker'}, {name: 'Niniane Wang', role: 'CTO, Minted'}, {name: 'Tim Berners-Lee', role: 'Web Developer'} ]; function createComponent() { var unit, clone, i = 0; // iterate creating tag per object for (i = 0; data.length > i; i += 1) { unit = data[i]; clone = createNametag(); clone.querySelector('.name').innerHTML = unit.name; clone.querySelector('.role').innerHTML = unit.role; document.body.appendChild(clone); } } function createNametag() { // Create the nodes var tmpl = document.createElement('div'), outer = document.createElement('div'), header = document.createElement('header'), p = document.createElement('p'), main = document.createElement('main'), name = document.createElement('p'), role = document.createElement('p'), footer = document.createElement('footer'); // add classes tmpl.classList.add('name-tag'); outer.classList.add('outer'); name.classList.add('name'); role.classList.add('role') // add content header.innerHTML = 'JSSummit>span>2014>/span>'; p.textContent = "Online JavaScript Conference"; footer.innerHTML = 'Follow us on Twitter @JSSummit>br/>Follow the conversation using #JSSummit'; // put it all together outer.appendChild(header); outer.appendChild(p); main.appendChild(name); main.appendChild(role); outer.appendChild(main); outer.appendChild(footer); tmpl.appendChild(outer); return tmpl; } createComponent();➹
<script id="myNametag" type="text/x-handlebars-template"> <div class="name-tag"> <div class="outer"> <header>JSSummit<span>2015</span> <p>Online JavaScript Conference</p> </header> <main> <p class="name">{{person.fullname}}</p> <p class="role">{{person.role}}</p> </main> <footer>Follow us on Twitter @JSSummit<br> Follow the conversation using #JSSummit </footer> </div> </div> </script>
<name-tag> <p class="role">Speaker</p> <p class="name">Estelle Weyl</p> </name-tag> <name-tag> <p class="name">Niniane Wang</p> <p class="role">CTO, Minted</p> </name-tag> <name-tag> <p class="name">Tim Berners-Lee</p> <p class="role">Web Developer</p> </name-tag>
Order of role / name doesn't matter
template (n) - A document or file having a preset format, used as a starting point for a particular application so that the format does not have to be recreated each time it is used.
-- WHATWG SpecificationThe template element is used to declare fragments of HTML that can be cloned and inserted in the document by script.
In a rendering, the template element represents nothing
querySelector()
contentDesigners can create rich web user interaction (UI) using CSS and HTML markup only. Developers can focus on integrating the design elements.
if('content' in document.createElement('template')) { // put template code here } else { //polyfill }
Clonable DOM that does nothing until activated
<template id="foo"> <style> /* styles scoped to template only </style> <div class="someclass"> template text here <div class="someotherclass"> <content></content> </div> </div> </template>
<template id="nameTagTemplate"> <style> .outer { border: 1px solid black; background: url(../assets/wood.jpg) cover rgb(222,122,91); width: 21rem; font-size: 2.5rem; text-align: center; font-family: Rockwell, serif; color: white; margin-bottom: 1rem; } header { font-weight: bold; padding: 10px 0 0 0; } header span { color: #ccc; font-weight: normal; } p, footer { font-size: 1rem; font-weight: normal; margin: -5px 0 10px 0; padding: 0; } main { background: white; color: black; font-family: sans-serif; font-size: 1.8rem; height: 12rem; padding-top: 0.2em; } .name, main:first-line { font-weight: bold; font-size: 2.3rem; margin-bottom: 1rem; } footer { margin: 10px 0 2rem; } </style> <div class="name-tag"> <div class="outer"> <header>JSSummit<span>2015</span> <p>Online JavaScript Conference</p> </header> <main> <content></content> </main> <footer>Follow us on Twitter @JSSummit<br> Follow the conversation using #JSSummit </footer> </div> </div> </template>
<template id="nameTagTemplate"> <style> .outer { border: 1px solid black; background: url(../assets/wood.jpg) cover rgb(222,122,91); .... ... footer { margin: 10px 0 2rem; } </style> <div class="name-tag"> <div class="outer"> <header>JSSummit<span>2015</span> <p>Online JavaScript Conference</p> </header> <main> <content></content> </main> <footer>Follow us on Twitter @JSSummit<br> Follow the conversation using #JSSummit </footer> </div> </div> </template>
<div class="name-tag"> <p class="role">Speaker</p> <p class="name">Estelle Weyl</p> </div> <div class="name-tag"> <p class="name">Ninian Wang</p> <p class="role">CTO, Minted</p> </div> <div class="name-tag"> <p class="name">Tim Berners-Lee</p> <p class="role">Web Developer</p> </div>
var hosts = document.querySelectorAll('.name-tag'); var template = document.getElementById('nameTagTemplate'); for(var i = 0, count = hosts.length; i < count; i++) { var clone = document.importNode(template.content, true); clone.querySelector('content').innerHTML = hosts[i].innerHTML; hosts[i].parentNode.appendChild(clone); hosts[i].remove(); } }
var clone = document.importNode(template.content, true);
var clone = template.content.cloneNode(true);
<content>: insertion point that projects the text from the shadow host to shadow root.
<template id="nameTagTemplate"> <style> ... </style> <div class="name-tag"> <div class="outer"> <header>JSSummit<span>2015</span> <p>Online JavaScript Conference</p> </header> <main> <content></content> </main> <footer>Follow us on Twitter @JSSummit<br> Follow the conversation using #JSSummit </footer> </div> </div> </template>
<div class="name-tag"> <p class="role">Speaker</p> <p class="name">Estelle Weyl</p> </div> <div class="name-tag"> <p class="name">Ninian Wang</p> <p class="role">CTO, Minted</p> </div> <div class="name-tag"> <p class="name">Tim Berners-Lee</p> <p class="role">Web Developer</p> </div>
var hosts = document.querySelectorAll('.name-tag'); var template = document.querySelector('#nameTagTemplate'); for(var i = 0; i < hosts.length; i++) { var shadow = hosts[i].createShadowRoot(); var clone = document.importNode(template.content, true); shadow.appendChild(clone); }
var clone = document.importNode(template.content, true);
var clone = template.content.cloneNode(true);
<content>: insertion point that projects the text from the shadow host to shadow root.
<content select=".classInHost"> looks inside the shadow host for any element with a matching class of .classInHost. If an element in the shadow host has the class of classInHost, its contents will be rendered in the shadow DOM.
<template id="nameTagTemplate"> <div class="name-tag"> <div class="outer"> <header>JSSummit<span>2015</span> <p>Online JavaScript Conference</p> </header> <main> <content select=".name"></content> <content select=".role"></content> </main> <footer>Follow us on Twitter @JSSummit<br> Follow the conversation using #JSSummit </footer> </div> </div> </template>➹
<div class="name-tag"> <span class="role">Speaker</span> <span class="name">Estelle Weyl</span> </div> <script> var host = document.querySelector('.name-tag'); var root = host.createShadowRoot(); var template = document.querySelector('#nameTagTemplate'); root.appendChild(template.content); </script>➹
<template> <div class="outer"> <header>JSSummit<span>2015</span> <p>Online JavaScript Conference</p> </header> <main> <content select=".name"></content> <content select=".role"></content> </main> <footer>Follow us on Twitter @JSSummit<br> Follow the conversation using #JSSummit </footer> </div> </div> </div> </template> <div class="name-tag"> <p class="role">Speaker</p> <p class="name">Estelle Weyl</p> </div> <div class="name-tag"> <p class="name">Ninian Wang</p> <p class="role">CTO, Minted</p> </div> <div class="name-tag"> <p class="name">Tim Berners-Lee</p> <p class="role">Web Developer</p> </div> <script> var el = document.querySelectorAll('.name-tag'); var template = document.getElementById('nameTagTemplate'); for(var i = 0; i < el.length; i++) { var shadow = el[i].createShadowRoot(); var clone = document.importNode(template.content, true); shadow.appendChild(clone); } </script>➹
<content></content> <content select=""></conent> <content select="*"></content>
* {color: red;}
<div class="spirit"></div> <script> var host = document.querySelector('.spirit'); var root = host.createShadowRoot(); root.innerHTML = "<p>Just because you see me," + "does that mean I really exist?</p>"; </script>➹
/* unknown elements are unresolved */ :unresolved { opacity: 0; /* hide until resolved } /* by default elements are always display:inline. */ :host { display: block; } /* target the parent node by class or id */ :host(.customElementsClass) { ... } :host(:hover) { opacity: 1; } :host(:active) { ... }
var root = host.createShadowRoot();
shadow root: 1st node of your shadow tree. Ancestor to all other nodes in the tree.
Shadow host content is not rendered. Rather, shadow root content gets rendered.
while the shadow root is not accessible via the DOM, it is manipulated exactly like the DOM:
document.querySelectorAll('.outer').length === 0
var header = document.createElement('h1'); root.appendChild(header);
<!--#exec cgi="/cgi-bin/template.pl" -->
if('import' in document.createElement('link')) { // you're good } else { // you need to polyfill }
<head>
....
<link rel="import" href="/path/file.html">
<link rel="import" href="//mysite.com/file.html">
<link rel="import" href="https://CORSEnabled.com/file.html">
</head>
<head>
....
<link rel="import" href="nameTagTemplate.html">
</head>
<!doctype html> <html> <head> <meta charset="utf-8"/> <title>Import a template</title> <script src="import.js"></script> <link rel="import" href="nametagtemplate.html" onload="loadHandler(event)" onerror="errorHandler(event)"> </head> <body> <div class="name-tag"> <p class="role">Speaker</p> <p class="name">Estelle Weyl</p> </div> <div class="name-tag"> <p class="name">Ninian Wang</p> <p class="role">CTO, Minted</p> </div> <div class="name-tag"> <p class="name">Tim Berners-Lee</p> <p class="role">Web Developer</p> </div> </body> </html>➹
<template id="nameTagTemplate">
<style>
.outer {
border: 1px solid black;
background: url(../assets/wood.jpg) cover rgb(222,122,91);
width: 21rem;
font-size: 2.5rem;
text-align: center;
font-family: Rockwell, serif;
color: white;
margin-bottom: 1rem;
}
header {
font-weight: bold;
padding: 10px 0 0 0;
}
header span {
color: #ccc;
font-weight: normal;
}
p, footer {
font-size: 1rem;
font-weight: normal;
margin: -5px 0 10px 0; padding: 0;
}
main {
background: white;
color: black;
font-family: sans-serif;
font-size: 1.8rem;
height: 12rem;
padding-top: 0.2em;
}
.name,
main:first-line {
font-weight: bold;
font-size: 2.3rem;
margin-bottom: 1rem;
}
footer {
margin: 10px 0 2rem;
}
</style>
<div class="outer">
<header>JSSummit<span>2015</span>
<p>Online JavaScript Conference</p>
</header>
<main>
<content select=".name"></content>
<content select=".role"></content>
</main>
<footer>Follow us on Twitter @JSSummit<br>
Follow the conversation using #JSSummit
</footer>
</div>
</template>
function loadHandler(e) { var content = document.querySelector('link[rel="import"]').import; var el = document.querySelectorAll('.name-tag'); var template = content.getElementById('nameTagTemplate'); for(var i = 0; i < el.length; i++) { var shadow = el[i].createShadowRoot(); var clone = document.importNode(template.content, true); shadow.appendChild(clone); } } function errorHandler(e) { console.log('Error loading import: ' + e.target.href); }➹
window.document
refers to main documentimportLinkFile.import
refers to the imported document where importedLinkFile
is link[rel=import]
<link>
must have rel="import"
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>What happens to imported content</title>
<script src="import.js"></script>
<link rel="import" href="template_textmediajs.html" id="foo">
</head>
<body><p>A template has been imported that has an H1, image and an alert, and we added some javascript!</p>
<script>
var myLink = document.getElementById('foo');
var myTemplate = myLink.import.querySelector('template');
var clonedTemplate = document.importNode(myTemplate.content, true);
document.querySelector('body').appendChild(clonedTemplate);
</script>
</body>
</html>
Inactive until activated:
imported <template>
<template>
<h1>This is some text</h1>
<img src="../assets/icons/icon-templates.png" alt="this is some media">
<script>alert("This is some javascript");</script>
</template>
<script> // the "importee", in this case the template var importedDoc = document.currentScript.ownerDocument; // the "importer", which has custom elements var parentDoc = document; parentDoc.body.appendChild(importedDoc.importNode(content, true)); </script>
<name-tag>
<p class="name">Estelle Weyl</p>
<p class="role">Speaker</p>
</name-tag>
<name-tag>
<p class="name">Ninian Wang</p>
<p class="role">CTO, Minted</p>
</name-tag>
<name-tag>
<p class="name">Tim Berners-Lee</p>
<p class="role">Web Developer</p>
</name-tag>
<div is="name-tag">
<p class="name">Estelle Weyl</p>
<p class="role">Speaker</p>
</div>
<div is="name-tag">
<p class="name">Ninian Wang</p>
<p class="role">CTO, Minted</p>
</div>
<div is="name-tag">
<p class="name">Tim Berners-Lee</p>
<p class="role">Web Developer</p>
</div>
The custom element that you import creates a shadow DOM from a <template> then registers itself.
registerElement(custom-element)
var CustomElement = document.registerElement('custom-element'); document.body.appendChild(new CustomElement());
or
registerElement(custom-element, prototype)
var CustomElement = document.registerElement('custom-button', { prototype: Object.create(HTMLVideoElement.prototype) });
var btnProto = Object.create(HTMLButtonElement.prototype); btnProto.createdCallback = function () { console.log('custom element was created'); } btnProto.attachedCallback = function (){ console.log('custom element was added') ; } btnProto.detachedCallback = function () { console.log('custom element was removed'); } btnProto.attributeChangedCallback = function (attr, old, newA) { console.log('attributes were altered'); } var btnProto = document.registerElement('my-button', { prototype: btnProto });➹
class MyButton extends HTMLButtonElement { createdCallback () { console.log('custom element was created'); } attachedCallback () { console.log('custom element was added'); } detachedCallback () { console.log('custom element was removed'); } attributeChangedCallback (attr, old, newA) { console.log('attributes were altered'); } } document.registerElement('my-button', MyButton);
var MyButton = document.registerElement('my-button', HTMLButtonElement);
var MyButton = document.registerElement('my-button', { prototype: Object.create(HTMLButtonElement.prototype) });
var YourButton = document.registerElement('your-button', { prototype: MyButton });
var myButton = new MyButton(); myButton.textContent = 'some content'; document.body.appendChild(myButton);
or, simply
<my-button>some content<my-button>
var yourButton = document.createElement('button', 'your-button'); yourButton.textContent = "I am extended" document.body.appendChild(yourButton);
which creates:
<button is="your-button">I am extended<button>
<style> my-element { display: block; } my-element:unresolved { opacity: 0; } my-element::shadow { /*target the shadow root */ } ::shadow p { /* any p within any shadow root */ } </style> <head> <body class="classInImporter"> <my-element></my-element> </body>
<template> <style> :host-context(.classInImporter) { ... } </style> .... <!-- html template for <my-element> --> </template>
<body class="classInImporter"> <my-element></my-element> </body>
<name-tag>
<p class="name">Estelle Weyl</p>
<p class="role">Speaker</p>
</name-tag>
<name-tag>
<p class="name">Ninian Wang</p>
<p class="role">CTO, Minted</p>
</name-tag>
<name-tag>
<p class="name">Tim Berners-Lee</p>
<p class="role">Web Developer</p>
</name-tag>
var nameTag = Object.create(HTMLElement.prototype);
nameTag.createdCallback = function() {
var template = document.getElementById('nameTagTemplate').content;
var clone = template.cloneNode(true);
var root = this.createShadowRoot();
root.appendChild(clone);
};
document.registerElement('name-tag', {
prototype: nameTag
});