Skating with Web Components

Trey Shugart

DogeScripter, Atlassian

@treshugart

slides.com/treshugart/skating-with-web-components/live

What are web components?

Web Components are a collection of standards that allow the building, and use of, reusable components made with HTML, CSS and JavaScript.

  • Templates
  • Shadow DOM
  • HTML Imports
  • Custom Elements

What are web components?

  • Chrome OS keyboard
  • Chrome OS media player
  • GitHub's <time is="relative-time"> element

What are Web Components?

<div> soup anyone?

What are Web Components?

Let's try that again.
<hangout-contacts>
  <hangout-contact
    src="dont-eat-the-yellow-snow.png"
    name="Frank Zappa"
    last-message="Frank: Freak Out!"
  ></hangout-contact>
  ...
</hangout-contacts>

Polymer

  • Google
  • Implemented through polyfills
  • Expands on it to provide data-binding and template expressions
  • Support is mostly usable in IE10, Safari 6 and Safari Mobile
  • Not small, 223k min (Platform + Polymer)
  • Not production ready
 * As of the 25th of June 

x-tags

  • Mozilla
  • Custom Elements through a proprietary API
  • Adds convenience
  • Nothing polyfilled by default
  • IE9+
  • 31.8k min

skate.js

Skate

  • Me
  • Loosely follows the custom element spec
  • Simple ShadowDOM-style templating
  • Custom Element and HTML Imports polyfill in development
  • Binds components using classes, attributes and tags
  • 6.7k min
  • Already in production!

Skating Classes

You've got some tabs

<ul class="tabs">
  <li><a href="#tab1">Tab 1</a></li>
  <li><a href="#tab2">Tab 2</a></li>
</ul>

But they only work on DOMReady

jQuery(function ($) {
  $('.tabs').tabs();
});

Just wrap it with Skate!

skate('tabs', {
  type: skate.types.CLASS,
  ready: function (element) {
    jQuery(element).tabs();
  }
});

skating attributes

Input or textarea needs to clear when ESC is pressed

<input type="text" data-clear-on-esc>

// or

<textarea data-clear-on-esc></textarea>

Create an attribute component!

skate('data-clear-on-esc', {
  type: skate.types.ATTR,
  events: {
    keyup: function (element, e) {
      if (e.keyCode === 27) {
        element.value = '';
      }
    }
  }
});

Skating Elements

You want an auto-complete element

<auto-complete url="some/service.json"></auto-complete>

Add a tag binding

var AutoComplete = skate('auto-complete', {
  type: skate.types.TAG,
  events: {
    'keyup input[type="text"]': function (element, e) {
      showSomeSuggestions(
        element.getAttribute('url'),
        e.target.value
      );
    }
  },
  template: '<input type="text">'
});

Element constructors!!

var autoComplete = new AutoComplete();
autoComplete.setAttribute('url', 'some/service.json');

What You Get

Class, attribute and tag bindings

skate('my-component', {
  // binds to classes, attributes and tags by default
  type: skate.types.ANY
});
  • ANY - class, attr or tag
  • TAG
  • ATTR
  • CLASS
  • NOTAG - class or attr
  • NOATTR - class or tag
  • NOCLASS - attr or tag

What You Get

Congruent API

Except:

// Component does not support custom elements.
var MyComponent = skate('my-component', {
  type: skate.types.NOTAG
});

// Will throw an exception!
var myComponent = new MyComponent();

What You Get

Lifecycle callbacks

skate('my-component', {
  ready: calledBeforeVisible,
  insert: calledAfterVisible,
  remove: calledAfterRemoval
});
* ready(), if specified, prevents element from showing until done

Async ready()

ready: function asyncReady (element, done) {
  doSomethingAsync().then(done);
}
* Specifying "done" makes callback async

What You Get

Prototype properties / methods

skate('my-component', {
  prototype: {
    someMethod: function () {
      // "this" refers to the element
      console.log(this);
    }
  }
});

Somewhere else...

document.getElementById('my-component').someMethod();

What You Get

Awesome attribute API

skate('my-component' {
  attributes: function handleAnyAttributeChange (element, change) {
    console.log(change);
    // type: 'insert, update or remove',
    // name: 'attribute-name',
    // newValue: 'new attribute value, undefined if just removed',
    // oldValue: 'old attribute value, undefined if just inserted'
  }
});

or

attributes: {
  'attribute-name': handleInsertAndUpdate
}

or even

attributes: {
  'attribute-name': {
    insert: handleInsert,
    update: handleUpdate,
    remove: handleRemove
  }
}

What You Get

Events

skate('my-component', {
  events: {
    click: function handleClick (element, e) {
      ...
    }
  }
});

Event delegation, Backbone style!

events: {
  'click .delegate[selector]': function handleDelegateClick (element, e) {
    // use `e.target` to get the delegate
  }
}

What you get

ShadowDOM-style templating by default

skate('my-component', {
  template: '<article>' +
    '<h2><content select=".heading"></content></h2>' +
    '<section><content></content></section>' +
  '</article>'
});

HTML before

<my-component>
  <span class="heading">My Heading</span>
  <p>First paragraph.</p>
  <p>Second paragraph.</p>
</my-component>

HTML after

<my-component>
  <article>
    <h2><span class="heading">My Heading</span></h2>
    <section>
      <p>First paragraph.</p>
      <p>Second paragraph.</p>
    </section>
  </article>
</my-component>

What You Get

Custom templating

skate('data-handlebars', {
  template: function (element) {
    var template = Handlebars.compile(element.innerHTML);
    element.innerHTML = template({
        title: 'Handlebars',
        body: 'However, you can use anything you want: React, Mustache, Jade, etc.'
      });
  }
});

HTML before

<div data-handlebars>
  <h1>{{ title }}</h1>
  <p>{{ body }} </p>
</div>

HTML after

<div data-handlebars>
  <h1>Handlebars</h1>
  <p>However, you can use anything you want: React, Mustache, Jade, etc.</p>
</div>

Thanks!

github.com/skatejs/skatejs

Made with Slides.com