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!
learn.jquery.com


One of the great aspects of jQuery is its extensibility, as evidenced by the many excellent plugins that have been developed for it.
blog.jquery.com

jQuery Plugins:

Gave US a Standard TO WRITE UI Libs



jQuery Plugins:

Encouraged US to Share UI Libraries



jQuery Plugins 
Aren't Perfect



  1. They have inconsistent or non-existent architecture
  2. 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?


  1. Easy to customize
  2. Not dependent on any libraries
  3. Compatible in AMD and non-AMD environments
  4. Usable by both developers *and* designers
  5. ....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/edit

Hide Private Functionality


Library developers should not be able to call:
marquee.moveItMoveIt(); 
...so let's hide it!


The Code: 
(Added _private)

Idempotent Constructor


Developers should be able to call:
var marquee = new Marquee(document.getElementById("marquee-me"));
var marquee2 = new Marquee(document.getElementById("marquee-me"));
window.setTimeout(function() { marquee.stop(); }, 5000); 
...without it doing everything twice.

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:

testable

UI libraries should be tested even *more* than normal code, 
because they are more likely to be reused and abused.



The Code:



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.
Bret Victor

JS UI Library Design

By pamelafox

JS UI Library Design

  • 14,221