Outline

We can define html tags which is encapsulated from page document using shadow dom.

Capability

can i use : http://caniuse.com/#feat=shadowdom

At the moment (2014/10/29) we can it only Google Chrome, Opera, Android Browser, and Chrome for Android

Benefit

  • ShadowDom make Dom tree encapsulation.
  • ShadowDom make CSS style encapsulation.

Dom tree encapsulation

HTML

<template id="nameTagTemplate">
	<div class="innerTemplate">
		<p>Hi! My name is <span class="name"><content select=".name"></content></p>
		<p>sex : <content select=".sex"></content></p>
		<p>live in : <content select=".live"></content></p>
	</div>
</template><div id="nameTag">
	<span class="name">Mike</span>
	<span class="sex">Male</span>
	<span class="live">London</span>
</div>

Javascript

var shadow = document.querySelector('#nameTag').createShadowRoot();
var template = document.querySelector('#nameTagTemplate');
var clone = document.importNode(template.content, true);
shadow.appendChild(clone);

// hidden from window.document.
// console.log(document.querySelector('#nameTag .innerTemplate .name')); // null
// console.log(document.querySelector('#nameTag .innerTemplate .sex'));  // null
// console.log(document.querySelector('#nameTag .innerTemplate .live')); // null

Result

Hi! My name is Bob
  • <content></content> is insertion point to show host(#nameTag) contents(Bob, Male, London). you can select value from host tag by using select attribute. (e.g. ) but you can select only top level tag in host tag.
  • you can get #nameTag element using document.querySelectorAll() but you can not get name, sex, and live values. it is hidden from window.document.

NOTICE:

Distributed nodes are elements that render at an insertion point (a element). The element allows you to select nodes from the Light DOM and render them at predefined locations in your Shadow DOM. They're not logically in the Shadow DOM; they're still children of the host element. Insertion points are just a rendering thing.

HTML5Rocks - Styling distributed nodes -

screenshot

CSS style encapsulation

You can define style for tags inside <template></template>. Those styles are not affected from page styles.

CSS

#buttons::shadow button {
	text-decoration: underline;
}
body /deep/ .name1{
	color: red;
}

HTML

<!-- --------------------------------------------- -->
<!-- Template -->
<!-- --------------------------------------------- -->
<template id="buttonTemplate">
	<style>  		
		button{color: blue;}
	    :host{ width: 200px; display: block; background-color: #999; text-align: center;}
		:host(.big) button{font-size: 1em;}
		:host-context(.theme-emphasis) button{text-transform: uppercase;}
		::content .name2{color: red;}
	</style>

	<button><content select=".name1"></content> button !</button>
	<button><content select=".name2"></content> button !</button>
</template><!-- --------------------------------------------- -->
<!-- host -->
<!-- --------------------------------------------- -->
<section class="theme-emphasis">

	<div id="buttons" class="big">
		<p class="name1">super</p>
		<p class="name2">ultra</p>
	</div>

</section>

JS

var shadow = document.querySelector('#buttons').createShadowRoot();
var template = document.querySelector('#buttonTemplate');
var clone = document.importNode(template.content, true);
shadow.appendChild(clone);

:unresolved

In this case, I don’t use it but if you create new element(it is not supported natively) the element might became Flash-of-Unstyled-Content (FOUC).

Before custom elements upgrade they may display incorrectly. To help mitigate FOUC issues, Polymer provides a polyfill solution for the :unresolved pseudo class.

REF: https://www.polymer-project.org/docs/polymer/styling.html

You can define style of element which is not define yet because html import dose not finish by using :unresolved.

inside template > style tag

You can define css styles for inside template tags.

e.g.

button{color: blue;}

:host

You can define host tag style.

e.g.

:host{ width: 200px; display: block; background-color: #999; text-align: center;}

:host(selector)

You can specify host using (selector).

e.g.

:host(.big) button{font-size: 1em;}

:host-context(selector)

You can specify parent of host using (selector). This is useful to define style that is based on page theme.

e.g.

:host-context(.theme-emphasis) button{text-transform: uppercase;}
<body class="theme-emphasis">
...
...
	<div id="buttons">
		<p class="name1">super</p>
		<p class="name2">ultra</p>
	</div>

In this case, You can define style for button in theme-emphasis using :host-context(.theme-emphasis).

::content selector

If you use some <content></content> tags you can define styles for certain <content></content> using ::content selector.

e.g.

::content .name2{color: red;}

Break encapsulation

You can also break css style encapsulatio from page styles using following ways.

::shadow

You can define style for shadow dom using ::shadow from page style.

e.g.

#buttons::shadow button {
	text-decoration: underline;
}

/deep/

You can define style for shadow dom using ::shadow from page style. In this case you don’t need to specify shadow host tag compared with ::shadow.

e.g.

body /deep/ .name1{
	color: red;
}

Result

See the Pen Shadow Dom Test by Tomoyuki kashiro (@Tkashiro) on CodePen.

Reference

Outline

We’ve used html template when we develop web app by using some tricks.

But now we can use template function safely and conveniently if you use new Template.

Capability

can i use : http://caniuse.com/#feat=template

At the moment (2014/10/27) we can not use it only IE. In all of modern browser we can use it.

Benefit

  • The markup is hidden and dose not rendered until activated.
  • Request to get any resources dose not work until activated.
  • You can not get inner contents of template element.
  • You can put template element in head, body, and frameset.

Usage

If you have following template you can use it with 2 ways below.

<template id="mytemplate">
	<div class="comment">this is new contents generated from template element.</div>
</template>

<section id="host"></section>

basic

var t = document.querySelector('#mytemplate');
var host = document.querySelector('#host');
var clone = document.importNode(t.content, true);

host.appendChild(clone);

with shadow dom

var t = document.querySelector('#mytemplate');
var host = document.querySelector('#host-target').createShadowRoot();
var clone = document.importNode(t.content, true);

host.appendChild(clone);

Reference

Outline

we can define new tag by using Custom Element and also extends native element.

Capability

can i use : http://caniuse.com/#feat=custom-elements

At the moment (2014/10/27) we can it only Google Chrome, Opera, Android Browser, and Chrome for Android

Introduction

In specification there are two type of custom elements.

Custom tag *

You can define new tag name which contain hyphen (-).It can be inherited other new tag.

e.g. x-hove, special-button

Type extension *

You can extend native tag (button, input, time). Those tags are called type extension.

e.g. special-button (extend button)

Custom tag

Register

define x-foo tag.

var XFoo = document.registerElement('x-foo');

define x-bar tag which inherit x-foo element.

var XBar = document.registerElement('x-bar', {
    	prototype: Object.create(XFoo.prototype)
  	});

NOTICE : According to HTML5Rocks, We can extends custom tag by using extends property. like this…

var XBar = document.registerElement('x-bar', {
    	prototype: Object.create(XFoo.prototype),
    	extends: 'x-foo'
  	});

But in Google Chrome Canary v40 it cause error below.

Error: Failed to execute ‘registerElement’ on ‘Document’: Registration failed for type ‘x-bar’. The tag name specified in ‘extends’ is a custom element name. Use inheritance instead. So you should use prototype inherit only. Please give me any information about it.

create

use in html.

<x-foo></x-foo>
<x-bar></x-bar>

create instance by using document.createElement.

var xfoo = document.createElement('x-foo');
document.body.appendChild(xfoo);

create new function.

var xbar = new XBar();
document.body.appendChild(xbar);

Type extension

Register

var SpecialButton = document.registerElement('special-button', {
	prototype: Object.create(HTMLButtonElement.prototype),
	extends  : 'button'
});

create

use in html. You have to set is attribute if you use Type extension.

<button is="special-button"></button>

create instance by using document.createElement.

var specialButton =  document.createElement('button', 'special-button');
document.body.appendChild(specialButton);

create new function.

var specialButton = new SpecialButton();
document.body.appendChild(specialButton);

Add Properties

use Object.defineProperty.

var xPiyoProto = Object.create(HTMLElement.prototype);
xPiyoProto.hoge = function(){alert('hoge');};
Object.defineProperty(xPiyoProto, 'bar', {value: 5}); // readonly
document.registerElement('x-piyo', {prototype: xPiyoProto});

add properties in definition.

var XPiyo = document.registerElement('x-piyo', {
	prototype: Object.create(HTMLElement.prototype, {
  		bar: {
    		get : function(){return 5;}
  		},
  		hoge: {
    		value: function(){alert('hoge');}
  		}
	})
});

Reference

jQuery.DeferredPipeline is jquery plugin to manage async function more easily.

I create a sample. please check it. :) thanks @Takazudo

You can check full version on my codepen page.

See the Pen BhCie by Tomoyuki kashiro (@Tkashiro) on CodePen.

Edit Gruntfile.js

Before

watch: {
      js: {
        files: ['<%= yeoman.app %>/scripts/{,*/}*.js'],
        tasks: ['newer:jshint:all'],
        options: {
          livereload: true
        }
      }
    }

After

watch: {
      options: {
        spawn: false // add spawn option in watch task
      },
      js: {
        files: ['<%= yeoman.app %>/scripts/{,*/}*.js'],
        tasks: ['newer:jshint:all'],
        options: {
          livereload: true
        }
      },

Result

50% speedup ( 4s -> 2s )

Before

Running "watch" task
Waiting...OK
>> File "app/styles/page/_about.scss" changed.

Running "compass:server" (compass) task
overwrite .tmp/styles/main.css (0.604s)
Compilation took 1.039s

Running "autoprefixer:dist" (autoprefixer) task
Prefixed file ".tmp/styles/main.css" created.

Done, without errors.


Execution Time (2014-02-24 02:57:19 UTC)
compass:server      3.9s  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 97%
autoprefixer:dist  120ms  ▇▇▇▇▇ 3%
Total 4s

After

Running "watch" task
Waiting...OK
>> File "app/styles/page/_about.scss" changed.


Running "compass:server" (compass) task
overwrite .tmp/styles/main.css (0.583s)
Compilation took 0.814s

Running "autoprefixer:dist" (autoprefixer) task
Prefixed file ".tmp/styles/main.css" created.

Running "watch" task
Completed in 2.063s at Mon Feb 24 2014 11:59:12 GMT+0900 (JST) - Waiting...

Why the watch task speedup

I think that the bottleneck of watch task is to execute this task in a child process. so if you set spawn option to false the watch task execute in a same context.

Attention

There is an attention in the original site

Whether to spawn task runs in a child process. Setting this option to false speeds up the reaction time of the watch (usually 500ms faster for most) and allows subsequent task runs to share the same context. Not spawning task runs can make the watch more prone to failing so please use as needed.