The Future of the Web


...and the one polyfill that makes it all possible today

Browser Support


WeakMap: Chrome, FF, IE11

Object.observe: Chrome 36

MutationObserver: all but IE10, Safari 6.1, Android 4.3

Web Components:  Chrome 36, FF 32, Android 4.4


WeakMap



keys are objects

values can be anything

does not prevent garbage collection of keys 

Use Cases



Associate data with an element w/out augmenting the element

Associate data with a read-only object

More efficient information hiding

e.g.


var map = new WeakMap();

var someEl = 
    document.getElementById('someEl');

map.set(someEl, {foo: 'bar'}); 

if (map.has(document.getElementById('someEl') {
    map.get(someEl).foo; // bar
} 

Object.observe


Receive callbacks when an object changes in any way

Native impl for Angular's scope.$watch

For Arrays: Array.observe

Angular will delegate in 2.0

Angular



scope.name = 'Ray';

scope.$watch('name', function(newV, oldV) {
    console.log('old name: ' + oldV);
    console.log('new name: ' + newV);
}); 

Native


scope.name = "Ray";

Object.observe(scope, function(change) {
    if (change.name === 'name') {
        console.log('old name: ' + 
            change.oldValue);

        console.log('new value: ' + 
            change.object[change.name]);
    }
}); 

MutationObserver



Receive callbacks when an element changes in any way

Element descendendants can also be observed

Replacement for inefficient Mutation Events

e.g.

var callback = function(records) {
    var record = records[0]; // for demo simplicity only
    var added = record.addedNodes;
    if (added.length) {
        var newTags = 0;
        for (var i = 0; i < added.length; i++) {
            if (added[i].className.indexOf('tag') >= 0) {
                newTags++;
            }
        }

if (newTags) { alert(newTags + " new tags added."); } } }; var observer = new MutationObserver(callback); observer.observe(tagsContainerEl, {childList: true});

Web Components


Templates

Custom Elements

Shadow DOM



Templates


Create re-usable content

<template> is not rendered, must be "activated"

Any elements can be children

<template>
    <div>hi there</div>
    <div>you can't see me right now</div>
    <blink>luckily for you</blink>
</template> 
Live example [jsbin]

HTML Imports


Pull another HTML file into the current document

Inject a packaged component (and all deps) w/ one line

Re-use templates stored in a central location

<link rel="import" href="/license.html">

Using an imported document


// Grab the <link> that imports the licensing blurb
var licensingLink = document.getElementById('licenseLink');
// Grab the relevant content from the imported docvar content = licensingLink.import.getElementById('license');
// Append the content to our doc document.body.appendChild(content);

NOTE: Server must return proper CORS headers for cross-domain imports

Complete example [jsbin]

Custom Elements


 <my-custom-element></my-custom-element>

MUST contain a dash
Take heed angular directive writers.  
Use ember?  You're cool. 

Why?
To avoid conflict w/ future standardized elements.

Creating Custom Elements


Initially an  :unresolved  HTMLElement

To "resolve", you must  document.registerElement


Two types:
  1. New* CE
  2. Type Extension CE

* I just made up this name

Standard Custom Element



<foo-bar>
   <!-- unresolved content -->
</foo-bar>

<script>
   document.registerElement('foo-bar');
</script>



Live example [jsbin]



Type Extension Custom Element



<video is="frame-grabber" src="somevideo.mp4"></video>

var frameGrabber = 
   document.createElement('video', 'frame-grabber');

frameGrabber.prototype.grab = function() {
   // grab current frame & do something w/ it
}

Shadow DOM


Allows creation of self-contained, sandboxed web components


A single Node can now be made up of 3 different subtrees:

  • Light DOM 
  • Shadow DOM
  • Composed DOM

Light DOM Tree


Part of the "logical DOM"

 Content of the custom element provided by integrator

<blink-is-back>
   Bring back the blink tag!
</blink-is-back>

Displayed pre-shadow-root render

Shadow DOM Tree


Part of the "logical DOM"
Internal to the custom element, not a child

Not directly accessible via the light DOM

  1. Include element on page
  2. Register (if CE)
  3. myElement.createShadowRoot();
  4. Append content to shadow root
  5. Live example [jsbin]

Composed DOM Tree


Content that is actually rendered

Pieces of the light and shadow DOM combined

Composed DOM from the <blink-is-back> CE:

<blink-is-back>
   <div style="text-decoration: line-through;">
      Bring back the blink element!
   </div>
   <div>But seriously, don't make text blink.</div>
</blink-is-back>

Polymer


Shim for all specs discussed in this presentation

Defers to native impls, when present

So, You can use all of these cool specs now!

The first devolving framework



Less important as Web Components et al. coverage increases

Should disappear when these specs are implemented everywhere


A polyfill for Shadow DOM?  How?

It's complicated

Everything is wrapped

Required to:
  • maintain encapsulation 
  • maintain separation between light & shadow DOM elements
  • retarget events originally destined for the shadow DOM tree

file-input



Web Component built using Polymer

Allows <input type='file'> to be easily styled

Built-in file validation

More intuitive access to (valid) files


e.g.


<file-input class="btn btn-primary" 
            extensions='["jpeg", "jpg"]' 
            minSize="500000" 
            maxSize="3000000">

   <span class="glyphicon glyphicon-file"></span>    Select a file

</file-input>

ajax-form


Web Component built using Polymer

Submit a form without a page reload

Send custom headers w/ form submit

Easy access to server response

Dirt simple validation support/control

Submit <file-input> elements too!

e.g.

<form is="ajax-form"
    method="POST"
    action="form/handler"
    headers='{"X-Cust-Header": "FooBar"}'>

    <input type="text" name="fullname" required>    <input type="submit">

</form>
form.addEventListener('submitted',
    function(e) {
        if (event.detail.status > 299) {
            // submit may have failed
        }
    }
);

form.addEventListener('invalid',
    function(e) {
        e.detail.forEach(function(badEl) {
            // do something w/ invalid element
        });
     }
 );

Useful Links


Made with Slides.com