Designing Javascript APIs for Usability

@rodneyrehm - rodneyrehm.de

simplicity, flexibility, stability

Being Simple

… doesn't necessarily mean easy

but consistent, obvious, predictable

Being Simple

… means being consistent (about arguments)

api.searchArray = function(haystack, needle) {
  // yadda yadda
};
api.searchMap = function(needle, haystack) {
// yadda yadda
};

Do. Not. Do. This.

Being Simple

… means being consistent (about names)

  • document.getElementById
  • indexedDB
  • element.innerHTML
  • XMLHttpRequest

The DOM is NOT ☹

Being Simple

… means aggregating things

api.search = function(haystack, needle) {
  if (isArray(haystack)) {
    // searchArray(haystack, needle);
  } else {
    // searchMap(haystack, needle);
  }
};

handle type switches internally!

Being Simple

… means aggregating things

api.value = function(newValue) {
  if (newValue === undefined) {
    return this.value;
  } else {
    this.value = newValue;
    return this;
  }
};

combining getters and setters

Being Simple

… means avoiding repetition

var elem = document.getElementById("foo");
elem.style.color = "red";
elem.style.background = "black";
elem.style.fontFamily = "Comic Sans";
var elem = document.getElementById("foo");
elem.setStyle("color", "red");
elem.setStyle("background", "black");
elem.setStyle("fontFamily", "Comic Sans");

Being Simple

… means avoiding repetition

var elem = document.getElementById("foo");
elem.setStyle({
  color: 'red',
  background: 'black',
  font: 'Helvetica Neue'
});

Rule of Thumb: you've got setters, you should probably accept maps

Being Simple

… means avoiding repetition

var elem = document.getElementById("foo");
elem.setStyle({}).doSomething().different();

Method Chaining

Being Simple

… means avoiding looping

var list = document.getElementsByTagName("ipnut");
for (var i=0, elem; elem = list[i]; i++) {
  var value = elem.getAttribute("data-default");
  elem.setValue(value);
}

Being Simple

… means avoiding looping

var list = document.getElementsByTagName("input");
list.setValue(function() {
  return this.getAttribute("data-default");
});

Rule of Thumb: you've got collections, you should probably accept callbacks

Being Simple

… means being obvious

event.initMouseEvent(
  "click", true, true, window, 
  123, 101, 202, 101, 202, 
  true, false, false, false, 
  1, null);

what's that 1 mean?

event.initMouseEvent({
  type: "click",
  canBubble: true,
  // …
  button: 1
});

oh, that I can understand ☺

Being Simple

… means keeping it simple!

Internal Frame Internal WTF?
Everything should be made as simple as possible – but not simpler.

-- Albert Einstein

Being Flexible

allow developers to extend your code

Being Flexible

… by using events

var event = jQuery.Event("widget:show");
this.$container.trigger(event);
if (event.isDefaultPrevented()) {
  // event handler prevented us…
  return this;
}

Control over bubbling and canceling!

Being Flexible

… by using callbacks

var ask = new Dialog({
  // some funky params
  init: function() {
      this.container.style.background = "red";
  }
});

Allow modification after the fact

Being Flexible

… by using callbacks

var ask = new Dialog({
  // some funky params
  show: function() {
    jQuery(this.container).fadeIn();
  }
});

Allow overwriting default behavior

Being Flexible

… by using hooks

jQuery.cssHook.colorInvertedBackground = {
  set: function(elem, value) {
    elem.style.color = value;
    elem.style.background = invertColor(value);
  }
};

jQuery(".something")
    .css("colorInvertedBackground", "#F7B519");

Allow extending specific features "globally"

Don't do the splits

Don't do the splits

Let users build edge-cases themselves!

Being Robust

thank you google

Being Robust

… means being clever enough!

jQuery(document).on("click", {});
view of chrome console showing an error

Being Robust

… means being clever enough!

function(callback) {
  var type = Object.prototype.toString.call(callback);
  if (type === "[object Function]") {
      throw new Error("callback must be a function!");
  }
}

Fail early! Don't fail silently!

Being Robust

… means not being too clever!

toggle() toggle(1000) toggle(1) toggle("1") toggle(true)

jQuery.toggle( duration )
// duration: A string or number determining 
// how long the animation will run.

jQuery.toggle( showOrHide )
// A Boolean indicating whether to 
// show or hide the elements.

toggle("1") === toggle("default"), obviously!

Being Robust

… means knowing your language!

function MyObject( /* expect object */ data) {
  this.data = data;
};
MyObject.prototype.say = function(key) {
  alert(this.data[key]);
};
var foobar = {
  hello: "bonjour!" // 75% of the French I know
};

var something = new MyObject(foobar);
something.say("hello");

foobar.hello = "salut!"; // the other 25%
something.say("hello");

run

Thank You!

Questions?

make things better

For more details, read the article on
SmashingMagazine.com


@rodneyrehm - rodneyrehm.de