The Long Road to Web Components
HTC stands for "HTML Components", a non-standard component definition API built on proprietary extensions of HTML and JScript/VBScript that was introduced in IE5.5 to allow developers to specify components with encapsulated behaviors and styles.
<PUBLIC:COMPONENT tagName="checkers">
<PUBLIC:PROPERTY NAME="boardWidth" />
<PUBLIC:METHOD name="newGame()" />
<PUBLIC:ATTACH event="onmouseover" onevent="mouseover()" />
</PUBLIC:COMPONENT>
<SCRIPT Language="Javascript">
function newGame(){
// insert code to initialize a new game here
}
function mouseover(){
// insert code to handle mouseover events
}
</SCRIPT>
<?IMPORT namespace="games" implementation="checkers.htc" >
<games:checkers />
Define Components:
Import Components:
Use Component:
The Long Road to Web Components
Result: Never standardized, removed in IE 10
The Long Road to Web Components
All was not lost! A new champion for Web Components was ready to throw down the gauntlet, enter: Dimitri Glazkov
In 2010, Glazkov started work on a new set of specs that would bring web devs the component APIs they long deserved.
The Long Road to Web Components
Custom Elements
HTML Imports
HTML Templates
Shadow DOM
Hooks for reactive composability live demo
document.registerElement('x-foo', {
prototype: Object.create(HTMLElement.prototype, {
bar: {
get: function(){ return 'test' }
}
})
});
document.registerElement('x-super-input', {
extends: 'input',
prototype: Object.create(
HTMLInputElement.prototype, {
// custom prototype properties
})
});
document.createElement('x-foo');
document.createElement('input', 'x-super-input');
document.registerElement('x-foo', {
prototype: Object.create(HTMLElement.prototype, {
createdCallback: { value: function(){} },
attachedCallback: { value: function(){} },
detachedCallback: { value: function(){} },
attributeChangedCallback: { value: function(){} }
})
});
Create your own elements with custom prototypes live demo
Extend existing, native elements live demo
Automatically bootstraps custom elements. No more $('.my-widget').myWidget() boilerplate and brittle DOM structures.
<x-foo>I'm an x-foo element!</x-foo>
<input is="x-super-input" />
Meet the Web Components Family
var myDiv = document.createElement('div');
document.body.appendChild(myDiv);
var root = myDiv.createShadowRoot();
root.appendChild(document.createElement('span'));
var span = myDiv.querySelector('span');
// this query will return NULL
div::shadow span { color: red; }
div /deep/ span { color: red; }
/* ::shadow selects 1st-level shadow content, while
/deep/ selects all spans at any shadow tree depth */
:host(:active) { color: red; }
/* the host node will have red text when :active */
:host-context(section) { color: red; }
/* host nodes in sections will have red text */
::content span { color: red; }
/* all spans within content insertion points */
Create encapsulated Shadow Roots live demo
Encapsulation semi-permeably blocks content access and style leakage
Encapsulation of shadowed content access and style targeting can be breached if a developer explicitly chooses to do so
div span { color: red; }
/* this style will not be applied */
var span = myDiv.querySelector('::shadow span');
// this query will return the shadowed span
root.innerHTML = '<content select="span"></content>' +
'<span>Foo</span>';
// only spans added to the host will be caught & shown
root.innerHTML = '<content></content><span>Foo</span>'
// all nodes added to the host will be caught & shown
Content elements dictate where injected elements are placed, and whether they are visible to the user
Meet the Web Components Family
<template id="titled_list">
<h2>
<content select="span"></content>
</h2>
<ul>
<content select="li"></content>
</ul>
</template>
var template = document.querySelector('#titled_list');
var clone = template.content.cloneNode(true);
document.body.appendChild(clone);
var div = document.createElement('div');
var root = div.createShadowRoot();
var clone = template.content.cloneNode(true);
var title = document.createElement('span');
title.textContent = 'My List';
root.appendChild(clone);
div.appendChild(title);
// the title span is captured by the content
// node and displayed inside the H2 element
Create your own, reusable templates using HTML markup
Generate copies of your templates
Inject your template copies into a Shadow Root to use their contents just as you would any other shadow content
Meet the Web Components Family
<head>
<link rel="import" href="/path/to/import.html">
</head>
<script async>
function importLoad(event) { }
function importError(event) { }
</script>
<link rel="import"
href="file.html"
onload="importLoad(event)"
onerror="importError(event)" />
<script async>
function importLoad(event) {
var doc = event.target.import;
var template = doc.querySelector('#widget_template');
// do something with your templates, assets, etc.
}
</script>
Import HTML sub-documents - fetches included scripts, styles, and templates
Act on import documents when they arrive, or fail due to an error
Utilize the imported resources when they arrive by accessing the import document attached to the link element
<!-- assume this is inside the imported sub document -->
<script>
var importDoc = document.currentScript.ownerDocument;
// this provides a hook to the imported sub-document
var mainDoc = document;
// the 'document' variable is a reference to the
// parent document that is bringing in the import
</script>
Access the importing parent document or the imported sub-document via script from within the import sub-document
Meet the Web Components Family
Native browser support is landing, but we'll still need polyfills for the foreseeable future
Summary:
A lightweight library focused on Custom Elements that wraps the imperative JavaScript APIs for Custom Elements to enable lightning-fast development of components for use in production sites today.
Key Features:
Summary:
A component and data-binding framework built atop Web Components APIs that offers a full suite of features. Comparable in size, depth, and breadth to projects like Angular and Ember.
Key Features:
Libraries and Frameworks
Libraries and Frameworks
(function(){
function executeTarget(e){
var targets = xtag.query(document, this.target);
var method = this.method;
var event = this.event;
(targets[0] ? targets : [this]).forEach(function(target){
if (typeof target[method] === 'function'){
target[method]();
}
if (event) xtag.fireEvent(target, event);
});
}
xtag.register('x-action', {
events: {
tap: executeTarget
},
accessors: {
target: { attribute: {} },
method: { attribute: {} },
event: { attribute: {} }
},
methods: {
execute: executeTarget
}
});
});
<polymer-element name="polymer-action"
attributes="target method event">
<script>
(function(){
function executeTarget(e){
var targets = Array.prototype.slice.call(
document.querySelectorAll(this.target)
);
var method = this.method;
var event = this.event;
(targets[0] ? targets : [this]).forEach(function(target){
if (typeof target[method] === 'function'){
target[method]();
}
if (event) {
target.dispatchEvent(
new CustomEvent(event)
);
}
});
}
Polymer({
created: function(){
this.addEventListener('tap', executeTarget);
},
execute: executeTarget
});
})();
</script>
</polymer-element>
"This will be a straight code-off, old school rules." - David Bowie
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: