about:me

Addy Osmani

Senior Developer Relations Engineer,

 

"Learning JavaScript Design Patterns"

"Developing Backbone.js Applications"

What if HTML was expressive enough to
support filling in the gaps in the web platform with our own elements?

Examples

In order to understand where we're going,
we need to understand where we've been.

Let's go back in time...

Get A T-Shirt!

Elements are the building blocks of the web

Elements are encapsulated

<select>
  <option>Small</option>
  <option>Medium</option>
  <option>Large</option>
</select>

Elements are configurable

<select id="schwag">
  ...
  <option disabled>Medium</option>
  <option disabled>Large</option>
  <option selected>XX-large</option>
</select>

<select size="4" multiple>
  <option>Do</option>
  <option>Re</option>
  <option>Mi</option>
  ...
</select>

Elements are composable

<select>
  <optgroup label="German Cars">
    <option>Mercedes</option>
    <option>Audi</option>
  </optgroup>
  ...
</select>

<form>
  <select name="size">
    <option value="s">Small</option>
    <option value="m">Medium</option>
    <option value="l">Large</option>
  </select>
</form>

Elements are programmable

var foo = mySelect.selectedIndex;

So what happened?

Tabs: a common component on the web

Building a tab component today

Pile on the JavaScript

Our markup is terrible

Seriously?

<x-tabs>
  <x-tab>Tab 1</x-tab>
  <x-tab>Tab 2</x-tab>
  <x-tab>Tab 3</x-tab>
</x-tabs>

Web Components are a set of emerging standards that allow developers to extend HTML.

Templates

Custom Elements

Shadow DOM

HTML Imports

Using a component today

<link rel="stylesheet" type="text/css" href="my-widget.css" />
<script src="my-widget.js"></script>
<div data-my-widget />
$(function() {
  $('[data-my-widget]').myWidget();
});
div.innerHTML = '<div data-my-widget />'
$(div).find('[data-my-widget]').myWidget();

Using a Custom Element

Import:

<link rel="import" href="my-widget.html" />

Consume:

<my-widget></my-widget>
div.innerHTML = '<my-widget />';

Creating Custom Elements

Register a new custom element my-element

var MyElement = document.registerElement('my-element');
<my-element></my-element>

Instantiate a custom element in JS

var myElement = document.createElement('my-element');
myElement.addEventListener('click', function(e) {
  alert('Thanks!');
});

Custom Elements can respond to change

Custom elements can respond to attribute changes

document.registerElement('my-element', {
  prototype: Object.create(HTMLElement.prototype, {
    attributeChangedCallback: {
      value: function(attr, oldVal, newVal) {
        this.innerHTML = '<h1>ATTRIBUTE CHANGED!</h1>';;
      }
    }
  })
});

Other lifecycle events:

  • createdCallback
  • attachedCallback
  • detachedCallback

Custom Elements can be extended

Extending native custom elements

var MegaButton = document.registerElement('mega-button', {
  // inherit the prototype of HTMLButtonElement
  prototype: Object.create(HTMLButtonElement.prototype)
});

Type extension custom element:

<button is="mega-button">


Custom Elements can encapsulate styles & DOM

Custom elements can have encapsulated styles

shadow = this.createShadowRoot();
shadow.innerHTML =
  "<style>span { color: green; }</style>" + "<span>I'm green</span>";
<my-element />
<span>I'm not green</span>

...and hidden internal DOM structures (shadow host)

<button>Hello, world!</button>
<script>
var host = document.querySelector('button');
var root = host.createShadowRoot();
root.textContent = 'How you doing?';

Custom Elements can be styled

Piece one level of the Shadow DOM (/shadow/) ~ upper shadow boundary

my-element /shadow/ h2{
  color: green;
}

Pierce every layer of the Shadow DOM (/shadow-deep/) ~ all shadow boundaries

my-element /shadow-deep/ h2{
  color: purple;
}

Cross-browser support improving but not there yet

Polymer is a library that uses the latest web technologies to let you create custom HTML elements.

Layers of Polymer

Elements

Reusable custom elements (in progress)

Polymer

An opinionated way to work with Web Components

Platform

Web Components polyfills for all
modern browsers

Native

The current browser landscape

Three ways to work with Polymer

Using elements

Creating elements

Utilizing the modern web platform

Using

polymer-project.org/docs/elements/

Everything is an element

Polymer UI elements

visual elements

<polymer-ui-accordion> demo

<polymer-ui-animated-pages>

<polymer-ui-card> demo

<polymer-ui-sidebar-menu> demo

<polymer-ui-tabs> demo

<polymer-ui-toggle-button> demo

<polymer-ui-theme-aware>

Salesforce elements

...

Reusability...the non-visual polymer-elements are used to implement these!

Everything is an element

Tabs revisited

<script src="platform.js"></script>
<link rel="import" href="polymer-ui-tabs.html">
<polymer-ui-tabs selected="0">
  <span>Home</span>
  <span>About</span>
  <span>Contact</span>
</polymer-ui-tabs>
Home About Contact

Everything is an element

Collapsible elements

<script src="platform.js"></script>
<link rel="import" href="polymer-ui-collapsible.html">
<polymer-ui-collapsible>
  <h3 class="polymer-ui-collapsible-header">Click Me!</h3>
  <div>
    some content...
  </div>
</polymer-ui-collapsible>

Click Me!

Realm of the galaxies at the edge of forever, made in the interiors of collapsing stars dispassionate extraterrestrial observer, rings of Uranus extraplanetary rich in heavy atoms shores of the cosmic ocean, white dwarf finite but unbounded!

Realm of the galaxies at the edge of forever, made in the interiors of collapsing stars dispassionate extraterrestrial observer, rings of Uranus extraplanetary rich in heavy atoms shores of the cosmic ocean, white dwarf finite but unbounded!

Polymer elements

non-visual utility elements

Layout

<polymer-layout>

<polymer-flex-layout>

<polymer-grid-layout>

View

<polymer-media-query>

<polymer-page>

Services / libs

<polymer-shared-lib>

<polymer-google-jsapi>

Data

<polymer-localstorage>

<polymer-xhr>

<polymer-jsonp>

<polymer-file>

<polymer-meta>

Behavior / interaction

<polymer-signals>

<polymer-selector>

Everything is an element

flexbox...using DOM

<script src="platform.js"></script>
<link rel="import" href="polymer-flex-layout.html">
<polymer-flex-layout vertical iscontainer>
  <div>Header</div>
  <div flex>Body</div>
  <div>Footer</div>
</polymer-flex-layout>
Header
Body
Footer

Everything is an element

AJAX...using DOM

<script src="platform.js"></script>
<link rel="import" href="polymer-ajax.html">
<polymer-ajax url="http://gdata.youtube.com/feeds/api/videos/"
              params='{"q":"chrome", "alt":"json"}'>
</polymer-ajax>
var ajax = document.querySelector('polymer-ajax');
ajax.addEventListener('polymer-response', function(e) {
  console.log(JSON.parse(this.response).feed.entry);
});
ajax.go();
Hit run...

Everything is an element

read files...using DOM

<script src="platform.js"></script>
<link rel="import" href="polymer-file.html">
<polymer-file readas="dataurl"></polymer-file>
var pFile = document.querySelector('polymer-file');

pFile.addEventListener('polymer-result', function(e) {
  console.log(this.result);
});

pFile.blob = new Blob(['abc'], {type: 'text/plain'}); // Set the file to read

pFile.read();

Everything is an element

responsive design...using DOM (Bonus)

<script src="platform.js"></script>
<link rel="import" href="polymer-media-query.html">
<polymer-element name="responsive-layout" attributes="responsive">
  <template>
    <polymer-media-query query="max-width:640px" queryMatches="{{isPhone}}"></...
    <template if="{{isPhone && responsive}}"> <!-- Phone markup -->
      <content></content>
    </template>
    <template if="{{!responsive}}"> <!-- Non-responsive case -->
     ...
    </template>
  </template>
  <script>Polymer('responsive-layout', {responsive: false});</script>
</polymer-element>
<responsive-layout responsive>
  <div>...</div>
</responsive-layout>

Creating

polymer-project.org/polymer.html

Declarative element registration

Custom elements without Polymer :(

<template id="template">
  <style>input { color: orange; }</style>
  <input type="text">
</template>

<script>
var proto = Object.create(HTMLElement.prototype, {
  createdCallback: {
    value: function() {
      var t = document.querySelector('#template');
      this.createShadowRoot().appendChild(t.content.cloneNode(true));
    }
  }
});

var MyInput = document.registerElement('my-input', {prototype: proto});
</script>

Declarative registration with it

<link rel="import" href="polymer.html">
<polymer-element name="my-element" noscript>
  <template>
    <style>h2 { color: orange; }</style>
    <h2>Hello from my-element!</h2>
  </template>
</polymer-element>
<my-element></my-element>

Declarative registration

<link rel="import" href="polymer.html">
<polymer-element name="hello-element">
  <template>
    <h2>I can say hello</h2>
  </template>
  <script>
  Polymer('hello-element', {
    sayHello: function() { alert('Howdy folks!'); }
  });
  </script>
</polymer-element>

Binding expressions

Binding Expressions

<polymer-element name="owner-element">
  <template>
    <h2>{{owner}} built me with Polymer</h2>
  </template>
  <script>
  Polymer('owner-element', {
    owner: 'Addy'
  });
  </script>
</polymer-element>
<owner-element></owner-element>

Published properties

Published properties (related)

<polymer-element name="owner-element" attributes="owner">
  <template>
    <h2>{{owner}} built me with Polymer</h2>
  </template>
  <script>
  Polymer('owner-element', {
    owner: 'Addy'
  });
  </script>
</polymer-element>
<owner-element owner="Alex"></owner-element>

Declarative event handlers

Declarative Event Handlers

<polymer-element name="click-element">
  <template>
    <button on-click="{{setMessage}}">Click me</button>
    <span>{{message}}</span>
  </template>
  <script>
  Polymer('click-element', {
    message: 'Waiting to be clicked...'
    setMessage: function() { this.message = 'I was clicked!' }
  });
  </script>
</polymer-element>

Automatic node finding

Automatic Node Finding

<polymer-element name="focus-element">
  <template>
    <button on-click="{{setFocus}}">Set Focus</button>
    <input id="nameInput" type="text">
  </template>
  <script>
  Polymer('focus-element', {
    setFocus: function() { this.$.nameInput.focus(); }
  });
  </script>
</polymer-element>

Accessibility isn't forgotten

Accessibility Checklist

  • Do all elements have meaningful text alternatives?
  • Can all functions be reached via the keyboard without using a mouse?
  • Do you have sensible focus order and focus target for each element?
  • Are your custom elements marked up to be accessible?
  • Can users understand everything without relying on color?
  • Is the moving or flashing content in your elements stoppable and safe?

ARIA roles with Polymer (Demo)

<polymer-ui-animated-pages id="pages" selected="{{page}}" ...>

<polymer-ui-toolbar aria-label="carousel buttons" 
on-keypress="{{keypressHandler}}" tabIndex="1">

  <div id="controls" aria-controls="pages">
    <template repeat="{{ item in itemList }}">
      <button aria-label="item-{{item.index}}" 
      tabIndex="{{item.index}}" alt="{{item.index}}" 
      on-tap="{{gotoPage}}">{{ labels? item.value : '&#8226;'}} 
      </button>
    </template>
  </div>

</polymer-ui-toolbar>

SEO friendly

The

polymer-project.org/docs/start/platform

The platform is a layer of polyfills that adds support for emerging standards, like Web Components, to all modern browsers.

Platform polyfills

supporting new web technologies today

Templates

HTML Imports

Custom Elements

Shadow DOM

Additional features include Mutation Observers, Pointer Events, Web Animations, and much more.

As browsers implement the specifications supported by the platform, the need for this
layer decreases.

...till eventually it's all gone.

Polymer Tooling

Helping you stay more productive

Hopefully you're excited.

Let's componentize the web.

Build elements. Your friends are gonna be like..