The Art of Developing Nano Widgets
Chris Barber
DojoConf 2011 • Sept.16.2011
About Me
- Chris Barber
- chris@cb1inc.com
- Software Engineer @ Appcelerator
- CEO/Technology Consultant @ CB1, INC.
- JavaScript, PHP, C++
- http://www.cb1inc.com/
- @cb1kenobi on Twitter, GitHub, Slideshare
- Dojo user since 0.3 (May 2006)
Warning!
Alpha Presentation Framework
Failure is an Option
The Problem
- dijit is big
- Lots of code
- Lots of widget instantiations
- Lots of overhead
- Not ideal for front pages
- I love dijit, but need to know when to use it
Size Matters
- Dojo 1.7 got bigger than 1.6
- 1.6.1 = 31.2K gzipped
- 1.7 = 39.9K gzipped
- Smaller files =
- Quicker downloading
- Quicker parsing
- Quicker execution
Definition: Nano Widget
- Same thing as a dijit widget, only smaller
- Only the functionality you NEED
- Overly optimized (i.e. brainfucked)
- Super hacky hacks
- Similar to "widgets" from other JS libraries
History: Nano Widget
- Wanted simple Lightbox
dojox.image.Lightbox is too big
- Uses dijit.dialog
- Only supports images
- Couldn't easily change the template
Wrote the dojox.image.LightboxNano in 2008
- "LightboxNano" sounded better than "Lightbox2"
- Only supports images, one image at a time, no captions
- Uber minimalist
Say Goodbye To…

IE6 has.js results
- dijit._Widget
- postCreate(), buildRendering(), postMixInProperties()
- Attach points & attach events
- Accessibility (sort of)
- Destroying widgets
- Programmatic control (disabled, selectChild(), etc)
- Pretty themes
- Old ass browser support
Sacrifice
Don't need it, don't add it, rip it out, win
What You Can Use
- dojo base
- declare()
- query()
- NodeList
- Event functions: connect(), on(), publish(), subscribe()
- DOM functions: create(), destroy(), place(), attr(), etc
Where To Use dijit
- Forms
- Validation
- Many awesome input controls (date picker, combobox, etc)
- Single Page Apps
- Rich widgets
Introducing The New Nano Widgets
New Nano Widgets!
- nano.Accordion
- nano.Tabs
- nano.Lightbox
- https://github.com/cb1kenobi/nano
Accordion: dijit vs. nano
Accordion: dijit vs. nano
|
dijit.layout.AccordionContainer |
nano.Accordion |
| Declarative & Programmatic |
|
|
| Animated |
|
|
| Keyboard Accessibility |
|
|
| Icons |
|
|
| Disablable |
|
|
| Easily Change Theme |
|
|
| Select Pane w/ JS |
|
|
| Old Ass Browser Support |
|
|
| # Widget Instances |
1 + 3 for each pane |
1 |
Accordion: dijit vs. nano
|
dijit.layout.AccordionContainer |
nano.Accordion |
|
| JavaScript |
22,087 bytes |
950 bytes |
23x smaller! |
| CSS |
6,287 bytes |
508 bytes |
12x smaller! |
| Total |
28,374 bytes |
1,458 bytes |
19x smaller! |
- Built using Closure Compiler
- Sizes after gzipping
- Does not include 39.9K for Dojo base
Tabs: dijit vs. nano
|
dijit.layout.TabContainer |
nano.Tabs |
| Declarative & Programmatic |
|
|
| Keyboard Accessibility |
|
|
| Icons |
|
|
| Tabs on Top |
|
|
| Tabs on Bottom or Sides |
|
|
| Sexy Tab Overflow Scrolling |
|
|
| Disablable |
|
|
| Easily Change Theme |
|
|
| Select Pane w/ JS |
|
|
| Old Ass Browser Support |
|
|
| # Widget Instances |
5 + 2 for each pane |
1 |
Tabs: dijit vs. nano
|
dijit.layout.TabContainer |
nano.Tabs |
|
| JavaScript |
33,080 bytes |
784 bytes |
42x smaller! |
| CSS |
7,187 bytes |
596 bytes |
12x smaller! |
| Total |
40,267 bytes |
1,380 bytes |
29x smaller! |
- Built using Closure Compiler
- Sizes after gzipping
- Does not include 39.9K for Dojo base
Lightbox: dijit vs. nano vs. nano
dojox.image.Lightbox
|
dojox.image.LightboxNano
|
nano.Lightbox
|
Lightbox: dijit vs. nano vs. nano
|
dojox.image.Lightbox |
dojox.image.LightboxNano |
nano.Lightbox |
| Declarative & Programmatic |
|
|
|
| Keyboard Accessibility |
|
|
|
| Mobile Support |
|
|
|
| Image Groups |
|
|
|
| Efficient with lots of images |
|
|
|
| Caption |
|
|
|
| Video Support |
|
|
|
| Old Ass Browser Support |
|
|
|
| # Widget Instances |
4 |
1 |
1 |
Lightbox: dijit vs. nano vs. nano
|
dojox.image.Lightbox |
dojox.image.LightboxNano |
nano.Lightbox |
|
| JavaScript |
29,013 bytes |
3,626 bytes |
1,137 bytes |
25x & 3x smaller! |
| CSS |
7,121 bytes |
0 bytes |
431 bytes |
16x smaller! |
| Total |
36,134 bytes |
3,626 bytes |
1,568 bytes |
23x & 2x smaller! |
- Built using Closure Compiler
- Sizes after gzipping
- Does not include 39.9K for Dojo base
Code & Hacks
and bad programming practices
Declaring Widgets
dijit Way
dojo.declare(
"my.widget",
dijit._Widget,
{
postCreate: function() {
this.domNode.innerHTML = "honey";
// magic goes here
}
}
);
Nano Way
dojo.declare(
"my.widget",
null,
{
constructor: function(params, node) {
node.innerHTML = "badger";
// magic goes here
}
}
);
Declarative Nano Params
<div data-dojo-type="my.widget"
data-param1="not cool"
data-dojo-props="param2:'cool'">
<div data-param3="cool" data-dojo-props="param4:'not cool'">
</div>
</div>
- param1 bad
- Widget not programatic friendly
- param4 bad
- Value must be manually eval'd
- Just pass in like param3 (which is less code anyways)
if Shortcuts
Not Nano
if (something) {
do(something);
}
if (something) {
name = "Colbert";
}
Nano
something && do(something);
something && (name = "Stewart");
Building Nodes
<div class="outer">
<div class="inner">Dojo!</div>
</div>
dojo.create("div", {
className: "inner",
innerHTML: "Dojo!"
}, dojo.create("div", {
className: "outer"
}, dojo.body()));
var c = dojo.partial(dojo.create, "div");
c({
className: "inner",
innerHTML: "Dojo!"
}, c({
className: "outer"
}, dojo.body()));
One-Time Event Firing
var h = dojo.connect(node, "onclick", function() {
dojo.disconnect(h);
// do stuff
});
define(["dojo/on"], function(on){
on.once(node, "click", function(evt) {
// do stuff
});
});
Templating
define([
"dojo/_base/declare",
"dojo/string",
"dojo/text!./templates/MyWidget.html"
], function(decl, str, template){
return decl("my.widget", null, {
constructor: function(params, node) {
node.innerHTML = str.substitute(template, {
name: "Agent Smith"
});
}
});
});
Do you need templating? Do you care about visual popping artifacts?
Closure Compiler is Smart
Before Build
define([
"dojo/_base/declare",
"dojo/dom-class"
], function(decl, css){
var baseClass = "nano";
return decl("nano.test", null, {
constructor: function(params, node) {
css.add(node, baseClass);
}
});
});
After Build (Closure Compiler)
define([
"dojo/_base/declare",
"dojo/dom-class"
], function(a, b){
return a("nano.test", null, {
constructor: function(a, c) {
b.add(c, "nano")
}
})
});
CSS3 Transitions vs. dojo.fx
- CSS3 Transitions over dojo.animateProperty()
- dojo.fx easy to work with (chaining, onEnd)
- dojo.fx more browser compatible
- CSS3 Transitions smoother
- CSS browser prefixes suck, but LESS kicks ass
- CSS3 Transition triggering quirkiness:
// .opacityTransition { transition: opacity 1s ease-in-out; }
dojo.style(node, "opacity", 0);
dojo.addClass(node, "opacityTransition");
setTimeout(function(){
dojo.style(node, "opacity", 1);
}, 0);
Build System
- Closure Compiler is Smarter, sometimes too smart
- Closure is generally better than Shrinksafe
- Create layers to optimize what code is loaded
- Accordion Layer Tests
- base + parser + fx = 45,191 bytes
- nano.Accordion = 1,197 bytes
- dijit.layout.AccordionContainer = 21,972 bytes
- base + parser + fx + nano.Accordion = 423 bytes less
- base + parser + fx + dijit.layout.AccordionContainer = 692 bytes less
Carefully Pre-Mature Optimize
Get the code working first
The Future of Nano Widgets
Nano Widget Radar
- Lightbox
- Grouping support
- Video playback support
- Rotator
- dojox.layout.RotatorContainer (dijit-based, do NOT use)
- Rewritten more nano as dojox.widget.Rotator (not dijit-based, use)
- Rotator is not "nano"... yet
Questions?
Nano Repo: https://github.com/cb1kenobi/nano
Twitter: http://twitter.com/cb1kenobi
Slides: http://talks.cb1inc.com/