Varunkumar Nagarajan
Powerful combo - HTML5, JS, CSS3
Thanks to HTML5, JS and CSS3, we can do amazing stuff on the web
Follow better engineering practices
Write code that is maintainable
The code must be
Build re-usable components
Any Object Oriented language will help us get there
How do we incorporate these on the web platform?
The fundamental problem is the lack of DOM encapsulation which could lead to
Libraries work around these problems by following certain assumptions and conventions
MV* frameworks help us to some extent


Method #1: "offscreen" DOM using [hidden] or display:none
<div id="mytemplate" hidden> <img src="logo.png"> <div class="comment"></div> </div>
<img>)#mytemplate.Method #2: manipulating markup as string. Overload <script>:
<script id="mytemplate" type="text/x-handlebars-template"> <img src="logo.png"> <div class="comment"></div> </script>
.innerHTML)Examples: handlebars.js, John Resig's micro-template script
Fundamental foundation of OOP
Separates code you wrote from the code that will consume it
We don't have it on the web!
Well, sort of:
<iframe> <iframe> are heavy and restrictive. ★ Seamless iframe
<template>MutationObserverObject.observe()calc()A collection of new capabilities on the browser
<template>Contains inert markup intended to be used later:
<template id="mytemplate"> <img src=""> <div class="comment"></div> </template>
<script>s don't run, images aren't loaded, media doesn't play, etc.
var t = document.querySelector('#mytemplate');
t.content.querySelector('img').src = 'http://...';
document.body.appendChild(t.content.cloneNode(true));
Is complex! Let's cover the basics:
@host at-rule
So...browser vendor's have been holding out on us!
<div id="host"> <h1>Varunkumar Nagarajan</h1> <h2>Hyderabad, India</h2> <div>...other content...</div> </div>
var host = document.querySelector('#host');
var shadow = host.createShadowRoot();
shadow.innerHTML = '<h2>Yo, you just got replaced!</h2>' +
'<div>Who? Varunkumar (@varunkumar)</div>';
// host.shadowRoot;
<style>s defined in ShadowRoot are scoped.
var shadow = document.querySelector('#host').createShadowRoot();
shadow.innerHTML = '<style>h2 { color: red; }</style>' +
'<h2>Yo, you got replaced!</h2>' +
'<div>Who? Varunkumar (@varunkumar)</div>';
Author's styles don't cross shadow boundary by default.
Change the behavior via properties resetStyleInheritance and applyAuthorStyles
@host at-rule)@host selects the shadow host element.
<style>
@host {
/* Gotcha: higher specificity than any selector,
lower specificity than declarations from a <style> attribute. */
* {
opacity: 0.2;
transition: opacity 400ms ease-in-out;
}
*:hover {
opacity: 1;
}
}
</style>
Widget author includes variable placeholders:
button {
color: var(button-text-color);
font: var(button-font);
}
Widget embedder applies styles to the element:
#host {
var-button-text-color: green;
var-button-font: "Comic Sans MS", "Comic Sans", cursive;
}
<div id="host"> <h1>Varunkumar Nagarajan</h1> <h2>Hyderabad, India</h2> <div>...other content...</div> </div>
that guy rendered as:
<div id="host">
#shadow-root
<style>h2 {color: red;}</style>
<h2>Yo, you got replaced!</h2>
<div>Who? Varunkumar (@varunkumar)</div>
</div>
...everything was replaced when we attached the shadow DOM
<content> elements are insertion points.select attribute uses CSS selectors to specify where children are funneled.<div id="host"> <firstName>Varunkumar</firstName> <lastName>Nagarajan</lastName> <city>Hyderabad, India</city> <div>...other content...</div> </div>
<style>
h2 {color: red;}
</style>
<hgroup>
<h2><content select="firstName"></content>
(@varunkumar)</h2>
<content select="city"></content>
</hgroup>
<content select="*"></content>
oninput, click)MutationRecords)MutationEvent performance / stability bottlenecks.
var observer = new MutationObserver(function(mutations, observer) {
mutations.forEach(function(record) {
for (var i = 0, node; node = record.addedNodes[i]; i++) {
console.log(node);
}
});
});
observer.observe(el, {
childList: true, // include childNode insertion/removals
//subtree: true, // observe the subtree root at el
//characterData: true, // include textContent changes
//attribute: true // include changes to attributes within the subtree
});
// observer.disconnect() // Stop observations
MutationEvent
Don't use Mutation Events!
// MutationEvent
document.addEventListener('DOMNodeInserted', function(e) {
console.log(e.target);
}, false);
// MutationObserver
var observer = new MutationObserver(function(mutations, observer) {
mutations.forEach(function(record) {
for (var i = 0, node; node = record.addedNodes[i]; i++) {
console.log(node);
}
});
}).observe(document, {childList: true});
Object.observe()
Object.observe : JS objects :: MutationObserver : DOM
function observeChanges(changes) {
console.log('== Callback ==');
changes.forEach(function(change) {
console.log('What Changed?', change.name);
console.log('How did it change?', change.type);
console.log('What was the old value?', change.oldValue );
console.log('What is the present value?', change.object[change.name]);
});
}
var o = {};
Object.observe(o, observeChanges);
// Object.unobserve(o, observeChanges); // Stop watching.
Object.freeze)about:flags.See Bocoup's writeup.
<template>)@host, styling hooks)Object.observe()WE DO :)
Define a declarative "API" using insertion points:
<element name="x-tabs">
<template>
<style>...</style>
<content select="hgroup:first-child"></content>
</template>
</element>
Include and use it:
<link rel="components" href="x-tabs.html">
<x-tabs>
<hgroup>
<h2>Title</h2>
...
</hgroup>
</x-tabs>
Define an imperative API:
<element name="x-tabs" constructor="TabsController">
<template>...</template>
<script>
TabsController.prototype = {
doSomething: function() { ... }
};
</script>
</element>
Declared constructor goes on global scope:
<link rel="components" href="x-tabs.html">
<script>
var tabs = new TabsController();
tabs.addEventListener('click', function(e) { e.target.doSomething(); });
document.body.appendChild(tabs);
</script>
Or, we can even extend existing elements
about:flagsabout:flagsYou don't have Shadow DOM enabled
You don't have Object.observe()
You don't have MutationObservers
<x-gangnam-style></x-gangnam-style>
( hover over the bar )
Web Components / Custom Elements
Shadow DOM
Mutation Observers / Object.observe
Thank you