Building Non-shit APIs with JavaScript
-
Upload
danwrong -
Category
Technology
-
view
12.560 -
download
1
description
Transcript of Building Non-shit APIs with JavaScript
Your JS LibraryDan Webb AKA @danwrong
Tuesday, May 3, 2011
Tuesday, May 3, 2011
In the beginning...
Tuesday, May 3, 2011
In the beginning...
Tuesday, May 3, 2011
$("p.surprise").addClass("ohmy").show("slow");
Tuesday, May 3, 2011
We all know what happened next...
jQuery
The Rest
Tuesday, May 3, 2011
Why?
Tuesday, May 3, 2011
Internals were not important...
Tuesday, May 3, 2011
...what had John created was a great interface
Tuesday, May 3, 2011
"All programmers are API designers"Joshua Bloch (http://lcsd05.cs.tamu.edu/slides/keynote.pdf)
Tuesday, May 3, 2011
The API is priority #1
Tuesday, May 3, 2011
❖ Predictability❖ Simplicity❖ Flexibility
Tuesday, May 3, 2011
Predictability
Tuesday, May 3, 2011
RTFMTuesday, May 3, 2011
Short attention spanTuesday, May 3, 2011
Think about your audience...
Tuesday, May 3, 2011
...use conventions people already know
Tuesday, May 3, 2011
Language conventions and standard library
Tuesday, May 3, 2011
THIS IS JAVASCRIPT
Tuesday, May 3, 2011
useCamelCase, yesReally.
Tuesday, May 3, 2011
Be careful with polyfills
Tuesday, May 3, 2011
Popular JS libraries
Tuesday, May 3, 2011
var paper = Raphael(10, 50, 320, 200);
var c = paper.circle(50, 40, 10);
c.attr("fill", "#f00");
c.show();
Tuesday, May 3, 2011
The problem domain
Tuesday, May 3, 2011
a.internal { color: #44e534; text-decoration: none;}
$('a.external').css({ color: '#44e534', textDecoration: 'none'});
Tuesday, May 3, 2011
Example: creating a DOM Builder
Tuesday, May 3, 2011
node.innerHTML = '<form method="post" action="/action">' + '<p>' + '<label>' + 'Username: <input type="text" name="username">' + '</label>' + '<label>' + 'Password: <input type="password" name="password">' + '</label>' + '</p>' + '</form>';
var form = document.createElement('form');var p = document.createElement('p');form.setAttribute('action', '/login');form.setAttribute('method', 'post');var usernameLabel = document.createElement('label');var usernameText = document.createTextElement('Username: ');var usernameInput = document.createElement('input');usernameInput.setAttribute('type', 'text');usernameInput.setAttribute('name', 'username');form.appendChild(p);p.appendChild(usernameLabel);// ... at this point I decided to give // all this up and become a farmer
Tuesday, May 3, 2011
var html = { element: function(name, attributes, children) { var node = document.createElement(name); for (var attr in attributes) { node.setAttribute(attr, attributes[attr]); } for (var i=0, len=children.length; i < len; i++) { node.appendChild(children[i]); } return node; }}
Tuesday, May 3, 2011
var form = html.element( 'form', { action: '/login', method: 'post' } [ html.element('p', {}, [ html.element('label', {}, [ document.createTextElement('Username: '), html.element('input', { type: 'text', name: 'username' }, []), // ... you get the idea ]) ]) ]);
Tuesday, May 3, 2011
var form = html.form({ action: '/login', method: 'post' }, [ html.p({}, [ html.label({}, [ document.createTextElement('Username: '), html.input({ type: 'text', name: 'username' }, []), // ... you still get the idea, right? ]) ]) ]);
Tuesday, May 3, 2011
function elementBuilder(name) { return function(attributes, children) { return html.element(name, attributes, children); }}
function generateBuilderFunctions(elements) { for (var i=0, len=elements.length; i < len; i++) { html[elements[i]] = createElementBuilder(elements[i]); }}
generateBuilderFunctions("p|div|span|strong|em|img|table|tr|td|th|thead|tbody|tfoot|pre|code|h1|h2|h3|h4|h5|h6|ul|ol|li|form|input|textarea|legend|fieldset|select|option|blockquote|cite|br|hr|dd|dl|dt|address|a|button|abbr|acronym|script|link|style|bdo|ins|del|object|param|col|colgroup|optgroup|caption|label|dfn|kbd|samp|var".split("|"));
Tuesday, May 3, 2011
Simplicity
Tuesday, May 3, 2011
Tuesday, May 3, 2011
Don’t make me RTFM again...
Tuesday, May 3, 2011
Sensible defaults
Tuesday, May 3, 2011
Tuesday, May 3, 2011
Tuesday, May 3, 2011
var evt = document.createEvent("MouseEvents");
evt.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
Tuesday, May 3, 2011
element.addEventListener('input', function() { // do some front-end magic}, false);
Tuesday, May 3, 2011
var evt = document.createEvent("MouseEvents");
evt.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
Tuesday, May 3, 2011
Use options hashes for optional arguments
Tuesday, May 3, 2011
evt.initMouseEvent("click", { bubble: false, relatedTarget: thing});
Tuesday, May 3, 2011
http://blog.rebeccamurphey.com/objects-as-arguments-in-javascript-where-do-y
Tuesday, May 3, 2011
Function calls should read well
Tuesday, May 3, 2011
// replace oldNode with newNode - DOM API oldNode.parentNode.replaceChild(newNode, oldNode);
// Dojo dojo.place(newNode, oldNode, "replace");
// jQuery$(oldNode).replaceWith(newNode);
Tuesday, May 3, 2011
Mask complexity if possible
Tuesday, May 3, 2011
var con = xd.connect({ src: 'http://danwebb.net/receiver' });
con.bind('ready', function() { rpc(con).call('getData', function(result) { alert(result); });});
Tuesday, May 3, 2011
xd.connect({ src: 'http://danwebb.net/receiver' }, function(con) { rpc(con).call('getData', function(result) { alert(result); });});
Tuesday, May 3, 2011
var con = xd.connect({ src: 'http://danwebb.net/receiver' });
rpc(con).call('getData', function(result) { alert(result);});
Tuesday, May 3, 2011
Back to the DOM Builder
Tuesday, May 3, 2011
var form = html.form({ action: '/login', method: 'post' }, [ html.p({}, [ html.label({}, [ document.createTextElement('Username: '), html.input({ type: 'text', name: 'username' }, []), // ... you still get the idea, right? ]) ]) ]);
Tuesday, May 3, 2011
var form = html.form({ method: 'post', action: '/login' }, html.p( html.label( 'Username: ', html.input({ type: 'text', name: 'username' }) ), html.label( 'Password: ', html.input({ type: 'password', name: 'pass' }) ), html.input({ type: 'submit', value: 'Login'}) ));
Tuesday, May 3, 2011
function elementBuilder(name) { return function() { var attributes = {}, children = [], args = Array.prototype.slice.call(arguments);
// if the first arg is not a element or a string then its an attributes hash if (!args[0].nodeType && typeof args[0] != 'string') { attributes = args.unshift(); } // children can be an array or remaining args if (Array.isArray(args[0])) { args = args[0]; }
// add rest of args as children converting any strings to text nodes for (var i=0, len=args.length; i < len; i++) { if (typeof args[i] == 'string') { children.push(document.createTextNode(args[i])); } else { children.push(args[i]); } }
return html.element(name, attributes, children); }}
Tuesday, May 3, 2011
Flexibility
Tuesday, May 3, 2011
Tuesday, May 3, 2011
Tuesday, May 3, 2011
Tuesday, May 3, 2011
Remember: you can't please everyone
Tuesday, May 3, 2011
Don’t try to second guess every use case
Tuesday, May 3, 2011
Options hashes != flexibility
Tuesday, May 3, 2011
28 options!Tuesday, May 3, 2011
Add hackability
Tuesday, May 3, 2011
public, internal, protected
Tuesday, May 3, 2011
var lib = (function() { var private = function() { // you can't mess with me };
return { _internal: function() { // you probably shouldn't mess with me }, public: function() { // I'm part of the API - call me sometime } }}());
Tuesday, May 3, 2011
...using functions
Tuesday, May 3, 2011
// https://github.com/ender-js/Ender
$._select = function(selector, root) { return Sizzle(selector, root);}
$._select = function (selector, root) { return (root || document).querySelectorAll(selector);});
Tuesday, May 3, 2011
..inheritance
Tuesday, May 3, 2011
// github.com/danwrong/loadrunner
function Mustache(path) { this.path = path;}Mustache.prototype = new loadrunner.Dependency;Mustache.prototype.start = function() { var me = this; $.get(this.path, function(result) { var template = Mustache.parse(result); me.complete(template); });}
using(new Mustache('thing.mustache'), function(template) { template.evaluate({ a: 'yep', b: 'nope' });});
Tuesday, May 3, 2011
..duck typing
Tuesday, May 3, 2011
Meanwhile, back in DOM Builder land...
Tuesday, May 3, 2011
function elementBuilder(name) { return function() { // code collapsed for clarity
// add rest of args as children // converting any strings to text nodes for (var i=0, len=args.length; i < len; i++) { if (typeof args[i] == 'string') { node = document.createTextNode(args[i]); } else { if (typeof args[i].toDOM == 'function') { node = args[i].toDOM(); } else { node = args[i]; } }
children.push(node); }
return html.element(name, attributes, children); }}
Tuesday, May 3, 2011
function Tweet(text, author, timestamp) { this.text = text; this.author = author; this.timestamp = timestamp;}Tweet.prototype.toDOM = function() { return html.p( { 'class': 'tweet' }, html.strong(this.author.name), this.text, html.span({ 'class': 'time' }, this.timestamp) );}
var timeline = // an array of Tweet objects
document.appendChild(html.div({ id: 'timeline' }, timeline));
Tuesday, May 3, 2011
Create a framework for solving your problem...
Tuesday, May 3, 2011
...then build your library on top of that
Tuesday, May 3, 2011
You got yourself a plugin system
Tuesday, May 3, 2011
Build tools to solve the problem
Tuesday, May 3, 2011
❖ Design up front❖ Make use of conventions❖ Don’t make me think❖ Build in hackability
Tuesday, May 3, 2011
Questions?@danwrong
Tuesday, May 3, 2011
@jointheflocktwitter.com/[email protected]
Tuesday, May 3, 2011
Eye of the BeholderTuesday, May 3, 2011
ChainingTuesday, May 3, 2011