Beyond jQuery Plugins:
JS UI Library Design
@pamelafox
BackboneConf 2013
Coursera
coursera + backbone
but this isn't a talk about backbone.
this is about
UI library design!
And no, not that kind of design.
in case that wasn't painfully obvious.
We started with
jQuery Plugins...
A Brief Timeline of jQuery Plugins
January, 2006:
jQuery released, with plugins built-in
January 25, 2006:
June 18, 2007:
September 17, 2007:
May 5, 2009:
jQuery plugins are totally rad
in many ways.
jQuery Plugins:
Encouraged US to Create UI Libraries
You can make your own plugins and use them privately in your code or you can release them into the wild. The barrier to creating a plugin of your own is so low that you'll want to do it straight away!
One of the great aspects of jQuery is its extensibility, as evidenced by the many excellent plugins that have been developed for it.
jQuery Plugins:
Gave US a Standard TO WRITE UI Libs
jQuery Plugins:
Encouraged US to Share UI Libraries
jQuery Plugins
Aren't Perfect
- They have inconsistent or non-existent architecture
- They depend on jQuery and often jQuery UI JS/CSS
A Thought Exercise:
What would
JS UI Libraries
look like if they were invented today?
- Easy to customize
- Not dependent on any libraries
- Compatible in AMD and non-AMD environments
- Usable by both developers *and* designers
- ....the list goes on.
Let's bring
<Marquee> back!
...as a modern
JS UI Library.
The Basic Library
The HTML:
<div id="marquee-me">Yo Wassssup!</div>
The Usage:
var marquee = new Marquee(document.getElementById("marquee-me"));
window.setTimeout(function() { marquee.stop(); }, 5000);
The Code:
http://jsbin.com/uhofov/7/editHide Private Functionality
Library developers should not be able to call:
marquee.moveItMoveIt();
The Code:
(Added _private)
Idempotent Constructor
Developers should be able to call:
...without it doing everything twice.var marquee = new Marquee(document.getElementById("marquee-me")); var marquee2 = new Marquee(document.getElementById("marquee-me"));
window.setTimeout(function() { marquee.stop(); }, 5000);
The Code:
(Added _private.getOrMakeMarquee)
Customization
Developers should be able to specify options:
var marquee = new Marquee(document.getElementById("marquee-me"),
{direction: 'forwards', distance: 100});
...And re-specify them:
window.setTimeout(function() {
var marquee2 = new Marquee(document.getElementById("marquee-me"),
{direction: 'backwards', distance: 1});
}, 2000);
The Code:
(Added _private.customizeMarquee)
Clear Defaults
Developers should have one place where they can see all the possible customization options and defaults, a la:
defaults: {
'direction': 'forwards', // 'forwards' or 'backwards',
'distance' : '10' // any integer
}
The Code:
(Added _private.defaults)
Declarative Customization
Designers should be able to configure in HTML:
<div id="marquee-me"
data-marquee-direction="backwards"
data-marquee-distance="5">
Yo Wassssup!
</div>
The Code:
(Changed _private.customizeMarquee)
Declarative Construction
Designers should be able to construct in HTML, too:
<div id="marquee-me"
data-marquee
data-marquee-direction="backwards"
data-marquee-distance="5">
Yo Wassssup!
</div>
The Code:
(Added _public.start)
Observable
Developers should be able to listen for events:
var marquee = new Marquee(document.getElementById('marquee-me'));
marquee.on('reverse', function() { document.getElementById('marquee-me').innerHTML = marquee.direction; });
The Code:
(Added EventTarget, .fire() calls)
AMD-COMPATIBLE
Developers should be able to use the library in AMD environments:
define("lib/marquee", function(Marquee) {
var marquee = new Marquee(document.getElementById('marquee-me'));
});
The Code:
(Added define block)
Declared dependencies
If there *are* any dependencies,
they should be explicitly declared and imported in a clean way.
Common dependencies:
- jQuery: dom, data() (http://jsbin.com/uhofov/47/edit)
- LucidJS: event emitting (http://jsbin.com/uhofov/49/edit)
- Q: promises
- Underscore: data manipulation, extend, debounce, etc.
testable
UI libraries should be tested even *more* than normal code,
because they are more likely to be reused and abused.
The Code:
One problem: How to Unit Test Private Functions?
DOCUMENTED
At minimum, the documentation should show example usage.
Bonus information:
- Where to find real usage examples in the codebase
- Why the library was written in the first place
- What can be improved in the future
The "Code":
When in doubt, document.
REAL WORLD EXAMPLES!
README
<div data-readme="watchlist-announcement" data-readme-show-count="1" data-readme-show-until-closed="data-readme-show-until-closed" data-readme-show-expires="Jun 15, 2013" class="hide readme">
We now give students the ability to "watch" classes they're interested in, which replaces the need for TBA sessions. <a href="https://class.coursera.org/mooc/forum/thread?thread_id=472" target="_blank" data-readme-close="data-readme-close">Read more here.</a>
<div data-readme-close="data-readme-close" class="readme-close-icon"><span class="icon-remove"></span></div>
</div>
new Readme(this.$('.readme'));
(Note: data-readme-close, cookie prefix)
MODALS
<div data-modal-overlay-class="coursera-overlay-dark" class="modal coursera-course-selfstudy-modal hide">
<div class="modal-header"><h2>What is "self study"?</h2></div>
<div class="modal-body"><p>Self-Study bla bla bla....</p></div>
<div class="modal-footer"><button data-modal-close="data-modal-close" class="btn btn-primary">OK, I got it!</button></div>
</div>
<a data-modal=".coursera-course-selfstudy-modal" role="button">?</a>
or
Modal(this.$('.coursera-course-self-study-modal')).open();
(Note: bootstrap optional, singleton enforced)
POPUPS
<li class="course-topbar-nav-list-item"
tabindex="0" role="button" aria-haspopup="true"
aria-expanded="false" aria-owns="course-topbar-aboutus"
data-popup="#course-topbar-aboutus"
data-popup-bind-open="mouseenter" data-popup-direction="se">
<a>About <i class="icon-caret-down"></i></a>
</li>
<div id="course-topbar-aboutus" class="course-topbar-sublist">
<a class="course-topbar-sublist-item" href="/about/jobs">Jobs</a>
<a class="course-topbar-sublist-item" href="/about/team">Team</a>
</div>
(Note: accessibility, click/touch, singleton enforced)
AND SO mUCH MORE!
- A/B framework
- Transloadit Uploader
- Rich Text Area
- Tooltips
- Calendar Date Picker
- Draggable/Sortable
- ...AND EVEN MORE!
js UI Library
Design Principles:
- Hidden private functionality
- Idempotent constructor
- Customizable with clear defaults
- Declarative declaration & customization
- Observable
- AMD-compatible (but not required)
- Optional declared dependencies
- Testable
- Documented
- Optional CSS
- Singletons enforced
- Accessibility built-in/documented
Improve the Developer Experience
sooooooooooooooo...
Couldn't you
do all that
with jQuery Plugins?
mos def.
You can apply those to anything. so do it!
...or don't, and tell us
what you do do.
It's good to learn how to do something. It's better to learn many ways of doing something. But it's best to learn all these ways as suggestions or hints . Not truth.
JS UI Library Design
By pamelafox
JS UI Library Design
- 14,221