This presentation uses https://code.google.com/p/io-2012-slides template

Basics:

  • Arrow keys (← →) to progress slide deck
  • P to bring up presenter notes (talking notes added to most slides post-presentation)
  • H to highlight relevant code on code slides
www.flickr.com/photos/25797459@N06/5438799763/

Introduction

  • A little bit about me
  • Things I enjoy about my job

Building Widgets

Store wrapped version of http://bl.ocks.org/mbostock/4061961

Building Widgets

Custom Business Expression Editor

Building Widgets

Rating widget, using (icomoon) icon fonts

What's this have to do with Web Components?

  • Web components are the HTML5 way of making Widgets
  • Fully bundled and encapsulated style + markup + code
  • Makes HTML arbitrarily extensible
  • Stuff you already know
  • Used Declaratively, in semantically meaningful way

Used Declaratively

A major benefit I like with Web Components is that they can be used declaratively.
That means:
  • Simple Mark-up, like we already know
  • No ondocumentready code to hook them up
  • But not only that, WYSIWYG

Widgets today expand

You ask for this ...fairly meaningful
<input id="date1" data-dojo-type="dijit/form/DateTextBox" />
		
	  

Widgets today expand

You get this. . .
<div class="dijit dijitReset dijitInline dijitLeft dijitTextBox dijitComboBox dijitDateTextBox dijitValidationTextBox" 
		id="widget_date1" role="combobox" aria-haspopup="true" data-dojo-attach-point="_popupStateNode" widgetid="date1">
	<div class="dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton dijitArrowButtonContainer" data-dojo-attach-point="_buttonNode" role="presentation">
	    <input class="dijitReset dijitInputField dijitArrowButtonInner" value="? " type="text" tabindex="-1" readonly="readonly" role="button presentation" aria-hidden="true">
	</div>
	<div class="dijitReset dijitValidationContainer">
	   <input class="dijitReset dijitInputField dijitValidationIcon dijitValidationInner" value="? " type="text" tabindex="-1" readonly="readonly" role="presentation">
	</div>
	<div class="dijitReset dijitInputField dijitInputContainer">
	    <input class="dijitReset dijitInputInner" type="text" autocomplete="off" data-dojo-attach-point="textbox,focusNode" role="textbox" tabindex="0" 
		          id="date1" aria-required="true" value="12/30/2005" aria-invalid="false">
	    <input type="hidden" name="date1" value="2005-12-30">
	</div>
</div>
		
	  

Or have little to no meaningful markup

You ask for this. . .
<body>
	<input id="myWidget" />
</body>

Or have little to no meaningful markup

You get . . .
<div style="margin-top: 10px; width: 250px; height: 25px;" id="myWidget" data-role="input" 
     role="textbox" aria-owns="calendarjqxWidgeta5d2dee7" aria-haspopup="true" 
     aria-readonly="false" class="jqx-widget jqx-widget-arctic jqx-datetimeinput jqx-datetimeinput-arctic jqx-input jqx-input-arctic millionOtherClasses. . . " 
     aria-valuenow="Tue Mar 04 2014 00:00:00 GMT-0600 (Central Standard Time)" 
     aria-valuetext="04/03/2014" aria-valuemin="Mon Jan 01 1900 00:00:00 GMT-0600 (Central Standard Time)" 
     aria-valuemax="Fri Jan 01 2100 00:00:00 GMT-0600 (Central Standard Time)" aria-disabled="false"
     aria-label="Current focused date is 3/4/2014 12:00:00 AM">
    <div class="jqx-max-size jqx-position-relative">
        <input class="jqx-position-absolute jqx-reset jqx-reset-arctic jqx-clear jqx-clear-arctic jqx-input-content jqx-input-content-arctic millionOtherClasses. . ." 
               id="inputdateTimeInput" autocomplete="off" type="textarea" name="dateTimeInput" 
               style="width: 230px; left: 0px; top: 0px; margin-top: 4px; text-align: left;">
        <div style="height: 100%; width: 19px; left: 231px;" 
             class="jqx-position-absolute jqx-action-button jqx-action-button-arctic 
                        jqx-fill-state-normal jqx-fill-state-normal-arctic jqx-rc-r jqx-rc-r-arctic">
            <div class="jqx-icon jqx-icon-arctic jqx-icon-calendar jqx-icon-calendar-arctic"></div>
        </div>
    </div>
</div>

What I want

Available via Web Components

The Web Component spec is comprised of a number of other specs:

  • Templates
  • Custom Elements
  • Shadow DOM
  • Imports
  • ... recently added Decorators, though no current spec ...
  • Each of these pieces are useful individually. In combination they allow for fully extendable, encapsulated, re-usable web components.

Templates

  • Inert Markup
    • Scripts aren't processed
    • Resources (such as images) aren't downloaded
    • Not rendered
  • Insert Contents of template into document to go live

Templates

<template id="commentTemplate">
    <div>
        <img src=""> <!-- no 404, we are inert -->
        <div class="comment-text"></div>
    </div>
</template>
	
		function addComment(imageUrl, text) {
		  var t = document.querySelector("#commentTemplate");
		  // clone it, we are gonna be re-using this template
		  var comment = t.content.cloneNode(true);
		  // Populate content.
		  comment.querySelector('img').src = imageUrl;
		  comment.querySelector('.comment-text').textContent = text;
		  // add to document, activating
		  document.body.appendChild(comment);
		}
	

Custom Elements

Foundational to custom widgets.

Provides developers a means to build there own, fully featured, first class DOM Elements

  • Inform Parser how to construct
  • Provide a lifecylce
  • Give elements a prototype (code)
  • Allows for inheritence (is-a)

Custom Elements

Inform Parser how to construct

Each document has an Element Registry. Parser can look up how to construct custom elements.

  • Unresolved elements are marked with :unresolved psuedo class (CSS)
  • Unknown Elements are resovled to HTMLUnknownElement and ignored like normal
  • Elements resolve once the element has been added to the documents element registry

Custom Elements

Custom Element Lifecycle

A custom element will have lifecycle events which can have callbacks.

  • Created
  • Attached
  • Dettached
  • AttributeChanged

Custom Elements

Prototype

A custom element can have a prototype, which is just a javascript object. In this way you can add custom methods and properties

Defining Custom Elements

<element name="my-custom">
    <script>
        ({
			myMethod: function(){/*...*/},
			myProperty: 0,
			myObjectProperty: null,
			createdCallback: function(){/*lifecycle method, hey, I should prob initialize properties here!!!*/},
			detachedCallback: function(){/*lifecycle method, hey I should probably clean up here */}
			
		});
    </script>
</element>
	

Defining Custom Elements

		var myCustom = document.registerElement('my-custom', {
		    prototype: Object.create(HTMLEleemnt.prototype, {
		        myMethod: function(){},
		        myProperty: 0,
		        myObjectProperty: null,
		        createdCallBack: function(){},
		        // ...
			
		    });
		
		});
	

Custom Elements can extend other elements

Native, or custom

<!-- declare -->
<element extends="button" name="my-button">
    ...
</element>

<!-- use -->
<button is="my-button">Click Me</button>
	

Shadow DOM

Foundational to DOM encapsulation.

Adjunct Tree of DOM nodes

  • Associated with a @host element
  • Not children of @host element (nor of @host document)
  • Subtrees form their own scope
    • IDs don't clash with parent document id's
    • Styles don't clash
  • Invisible (penetrable) boundaries

@host element

A shadow DOM is hosted by a non-shadow element. CSS in shadow element can reach out and style its host element using @host.

			// why not use a template
			var template = document.querySelector("#myTemplate").cloneNode(true);
			// grab host
			var host = document.querySelector("#myNode");
			// attach shadow DOM to host
			var shadowDom = host.createShadowRoot();
			// treat shadow dom like any other dom
			shadowDom.appendChild(template);
		

Shadow DOM

Seperate But equal (or something)

A shadow DOM isn't part of the document hierarchy. Its elements aren't reachable (and don't clash) from/with outside. Only inherited styles (overridable) penetrate through the shadow DOM boundary. And only certain events bubble out of shadow dom, and only after being re-target to host element.

Shadow DOM

Can be very complicated, very cool

Shadow DOM's content can be distributed through multiple insertion points

			<!-- my-custom shadow-->
			<template>  
			    <style>
				        @host {/* reach up to my host style*/}
				        *::distributed(h2){/*decend down into my contents boundaries*/}
            </style>
                <content select="h2">
				    <!-- only content that is in a h2 element when injected will be placed here, and selector will do -->  
				    fallback content, only here if no h2 inserted 
				</content>
				<content>
				    <!-- all content not previously selected will be injected here -->
					
				</content>
			</template>
		

Shadow DOM

Can be very complicated, very cool

Shadow DOMs can have there own shadow DOMs

In general The most recently applied (youngest) shadow DOM has priority over older ones. But that is a story for another time.

Imports

Foundational to being able to use Web Components

All and all the most straight forward of the batch.

    <!-- import a declarative Web Component  -->
    <link rel="import" href="path/to/my-component.html">

W3C Specifications

Good Tutorials

In Use

<Thank You!>