jQuery’s Best Friends

by Alex Sexton

Yes, I'm the guy that wrote GOTO in Javascript...

Goto raptor

Yes, I also created html5r0cks.com and partofhtml5.com

Yes, I have a domain name registration problem...

Who needs friends anyways‽

Because jQuery is great at this:

	      $('#treasure').hide();

Because jQuery is aiight at this:

$.fn.a = function(){
  return this.append(' Effin Eh!');
};

But jQuery is pretty bad about this:

// The Mighty Ducks Flying V
function knucklePuck() {
  function() {
    function() {
      function() {
        // omg inception.
      }
    }
  }
}();

Srsly guys.

Just because jQuery doesn't decide on a application structure for you, it doesn't mean that you don't need one.

RequireJS

RequireJS is a module loader (@<3).

  • Consider it your bootstrapper
  • 4.9KB minified and gzipped (on avg)
  • CommonJS Compliant(ish)
  • Uses modules
  • Nested Dependency Management
  • Serverside (Javascript on Java) Build Tool for Optimization
  • Asynchronous. (Faster than regular script tags)
  • Plugin System for extending loading methods and types

LABjs

LABjs is a script loader.

  • A replacement for the script tag
  • 2KB but with a 640B bootstrapper - You should probably inline this...
  • FAST
  • Easy (but) Manual Dependency Management
  • No code modifications || Style rules, etc...
  • Asynchronous. Load in Parallel. Execute In Order.

HINT

If you aren't using a script loader:
YOU SHOULD BE.

Using RequireJS: Getting Started

<!DoCTyPe html>
<html>
<head>
  <title>Paul Irish Fan Club</title>
  <script>
    // Load my application
    require(["myApp"]);
    // omg that's it?!?!
  </script>
</head>
// ... 

RequireJS: Actually Getting Started

require.def("myApp", 
['lib/jquery', 'utils/underscore'], 
function($, _) {

 $(function(){
  _(["rebecca", "adam"]).each(function(n){
   alert(n + " is my BFF!");
  });
 });

});

Objects: non-moduley way.

function Person(opts) {
  // Anything accessible goes on `this`
  this.func1 = function(){...};
  this.name = opts.fname + opts.lname;
}

// The function runs, and 
// returns a new instance of `this`
var myPerson = new Person({
  fname: 'Alex',
  lname: 'Sexton'
});

Modules: WTF are they?

function Person(opts) {
  // normally a constructor function
  // returns `this` (inherently)
  return {
    obj   : function(){...},
    name  : opts.fname+' '+opts.lname
  };
}
var myPerson = new Person({
  lname: 'Sexton',
  fname: 'Alex'
});

RequireJS: A Module!

require.def("myModule", 
['lib/jquery'], 
function($) {
  return {
    doSomething : function(){...},
    lolify      : function(){...}
  };
});

The dependencies that you require will execute in no specific order, as they shouldn't be dependent on each other.

RequireJS: Using A Module!

require(["myModule"],
function(myModule) {
  // Use the API that you built!
  myModule.doSomething();
  myModule.lolify();
});

It doesn't matter how deeply nested your dependencies are, they will execute in the order that they need to. Ideally, nothing leaks into the global namespace, unless you force it to.

RequireJS: Extensibility

require(['order!a.js', 'order!b.js'],
function(){
  // b needs a like
  // IE6 needs death.
});

You can do a lot with a few officially supported plugins:

  • Mimic the LABjs method
  • Load text, CSS, and JSON dependencies
  • Load whole i18n bundles (omgwow)

RequireJS: Advanced Usage

require.def('factory', 
(Object.create) ? [] : ['fill/objCreate'],
function() {
  var myCoolObject = {
    func1: function(){ /* Rebecca */ },
    func2: function(){ /* does this. */ }
  };
  // return a function that returns an obj
  return function(){
    return Object.create(myCoolObject);
  };
});

LABjs: Script Tag 2.0

$LAB
    .script('/js/json2.js')
    .script('/js/jquery.js').wait()
    .script('/js/jquery-ui.js')
    .script('/js/vapor.js');
  • LAB === Loading And Blocking JS
  • Now it should be called SAWjs, but that's ok.
  • Downloads everything at once, only waits to execute between `waits`

LABjs: Inline Script Replacement

<script src="/js/jquery.js"></script>
<script>$('body').load('zombo.html');</script>

<!-- becomes -->
<script>
$LAB
  .script('/js/jquery.js').wait(function(){
      $('body').load('zombo.html');
  });
</script>

LABjs: Advanced Usage?

var myLabChain = $LAB
  .script('/js/jquery.js').wait(function(){
      $('body').load('zombo.html');
  });

// some code or html here
document.write('this is not async');

// this will remember everything from the
// first part and wait til it's done
myLabChain.script('/js/goto.js');

SUPER FAMOUS QUOTE

“The fastest loading script is the one that you never request.”

- Leonardo Davinci

YepNopeJS: A LABjs Wrapper

yepnope([
 {
   test: Object.create,
   yep : ['script1.js', 'style1.css'],
   nope: ['shim.js', 'style2.css'],
   both: ['jquery.js']
 }
]);
  • Uses LABjs chain to keep dependencies in line.
  • Conditionally loads things based on tests!

Modernizr: Feature Test The Future

  • Does not implement HTML5 or CSS3
  • Tests to see if features exist
  • Ok, wise-guy, it has the html5shi(m|v) in it...
  • 5.0 kb gzipped
  • Must load at top (synchronously), but it's fast!
  • Use in tandem with Polyfills
  • Works great with Yepnope!

Modernizr: Other Features

<script>if (!Modernizr.websockets) { alert('go away!'); }</script>
<style>
#notice {  display:none; }
.no-js #notice { display:block; }
.websockets #sojustcloud {
  display:block;
}
</style>
  • Gives you the Modernizr object, with the test results on it.
  • Adds classes to the html or body element based on feature tests

Modernizr + YepNope === BFFs

yepnope({
   test: Modernizr.geolocation,
   yep : ['/js/geo.js'],
   nope: ['google-maps-api.js'],
   callback: function(file, result){
     if (!result) {
       loc = goog.getLocation();
     }
   }
 });
  • Use the callback to run more conditional code...

Vapor.js

Vapor Stats

vapor js

Let's Talk Templates!

OK. Don't get mad, but...

A Rumor

$('.container').html('<div>' +

  'I heard' + 
  ' that John Resig </div>' +

  '<em>cries</em> himself' +
  
  ' to sleep, when he sees you ' +
  'do this..... D: ');
  • "Hey can you change all the widgets to have a wrapper class?" --- "uh, oh..."

MicroTemplates (minimally)

<ul>
<%
_.each(products, function(prod){
%>
  <li>
    <a href="#"><%= prod.name %></a>
  </li>
<%  
});
%>
</ul>

Where to put templates

In your markup:

<script id="tmpl-t1" type="text/html">
  <h1><%= data.title =></h1> 
</script>

In your JS app:

// Get the markup out
var t = $('#tmpl-t1').html();
// Compile a template
var tmpl = _.template(t);
// render it
tmpl({data: {title : 'Ralph Smells'}});

Templates: Some Notes

  • I don't really care which you use, they're all the same...
  • Resig's Microtemplates, Mustache, Haml, & jq-tmpl are popular. (Yehuda's Handlebars looks cool...)
  • Garann Means had a talk on this at jqcon, check it out (when the vids are released).
  • Always compile your templates. Always.

RequireJS & Templates & You!

You can load your templates as text dependencies!

require.def('myApp', ['text!tmpl/main.tmpl'],
function(mainTmpl) {
  // Compile a template
  var tMain = _.template(mainTmpl);
});
  • If you use the build tool, this is highly optimized.
  • This duplicates the compile step, so you could use a singleton :)

Last thing on Templates...

var cache = {};
function render(name, cntxt) {
  if (!cache[name]) {
    cache[name] = _.template(
      $('#tmpl-'+name).html()
    );
  }
  return cache[name](cntxt);
}
  • Applies a dom prefix convention of id="tmpl-name"
  • Now you don't have to think about the dom, and compilation comes free.

Uninspired By Iceland
(An Interlude)

Underscore.js: JS Utility-Belt

  var
    nativeForEach      = ArrayProto.forEach,
    nativeMap          = ArrayProto.map,
    nativeFilter       = ArrayProto.filter,
    nativeIndexOf      = ArrayProto.indexOf,
    nativeIsArray      = Array.isArray;
    // etc
  • The bow-tie to jQuery's tux... or something
  • Uses native functions when available
  • Makes. Your. Code. Beautiful.

Underscore.js: Functional <3z

_(["apples", "oranges"])
  .each(function(fruit) {
    alert('I love ' + fruit);
  });
  • Ever need: each, map, reduce, reduceRight, detect, select, reject, all, any, include, invoke, pluck, max, min, sortBy, sortedIndex, toArray, size first, rest, last, compact, flatten, without, uniq, intersect, zip, indexOf, lastIndexOf, range bind, bindAll, memoize, delay, defer, wrap, compose keys, values, functions, extend, clone, tap, isEqual, isEmpty, isElement, isArray, isArguments, isFunction, isString, isNumber, isBoolean, isDate, isRegExp isNaN, isNull, or isUndefined ???
    Oh yea, it has Resig's MicroTemplates built in too :)

Underscore Tip

Delete your utils.js file, and start using underscore.

PubSub: Decoupled by Design

$.subscribe('something', function(a, b){
  console.log(a, b);
});

$.publish('something', ['eh', 'bee']);

// Yay, it logged!
  • A lot like custom events, but more simple. No relation to DOM.
  • Dojo did it. BUT - Pete Higgins ported it for us.
  • You can implement this in jQuery with Custom Events!

PubSub: What's the point?

  • Make it a point to understand the types of things that should be announced.
  • Get in the habit of announcing them before you need to.
  • When the boss asks you to popup an alert after every 4 times data loads:
var count = 0;
$.subscribe('dataLoad', function() {
  count++;
  if (count % 4 === 0) {
    alert(count);
  }
});
     

Resig's Simple Inheritance

var Person = Class.extend({
  init: function(isDancing){
    this.dancing = isDancing;
  },
  dance: function(){
    return this.dancing;
  }
});
  • Mimics Classical Inheritance - which is fine, if you like it.
  • Lightweight, easy-to-understand.
  • Nice additions like _super() and good `typeof` support

Resig's Simple Inheritance

var Ninja = Person.extend({
  init: function(){
    this._super( false );
  },
  dance: function(){
    // inherited version of dance()
    return this._super();
  },
  swingSword: function(){
    return true;
  }
});

Prototypal YAY-heritence.

var aPersonLikeObject = {
  init : function() {...},
  dance: function() {...}
};
var anotherPersonOrWhatever = Object.create(aPersonLikeObject);

anotherPersonOrWhatever.dance();
  • Differential inheritance by default.
  • No classes.
  • Harder to come up with cool metaphors like Classical inheritance

Prototypal YAY-heritence.

prototypal magic

Inheritance in Plugins

TOO MUCH FOR THIS SLIDE ---->
var orWhateverable = {
  init: function(options, elem) {
    this.options = $.extend({}, this.options, options);
    this.$elem = $(elem);

    return this;
  },

  options: {...},
  
  addOrWhatever: function() { 
    this.$elem.append(' or whatever...');
  }
};

$.fn.orWhateverable = function(opts) {
  return this.each(function(){
   $.data(this, 'orWhat', Object.create(orWhateverable).init(opts, this));
  });
};

Differential Inheritance In Plugins

With that code: you share a single object as the definition for all instances of your plugin (per matched element).

Modifying Plugin Functionality

// Assume orWhateverable from before is here
$('div').orWhateverable();

$('div').each(function(){
  $(this).data('orWhat').addOrWhatever();
}); 
// or whatever... added to all divs.

orWhateverable.addOrWhatever = function(){
  this.$elem.prepend('Yo dawg, ');
};

$('div').each(function(){
  $(this).data('orWhat').addOrWhatever();
});
// Yo dawg, added to all divs.

EasyXDM: An Overview

var socket = new easyXDM.Socket({
  local: "name.html",
  onReady: function(){
    // you need to wait for the onReady callback before using the socket
    socket.postMessage("foo-message");
  },
  onMessage: function(message, origin) {
   alert("received " + message + " from " + origin);
  }
});
  • Implements the postMessage API
  • Falls back to _everything_
  • Kind of a perfect solution for Cross Domain communication. <3z

Imagine

Imagine a world where the core of your application is the code that you wrote, instead of code that John Resig wrote...

Namespaces

You deserve your own namespace. Get off jQuery's. You are an app. Declare yourself as such.

Friends of Friends

  • Check out Backbone.js by Document Cloud
  • jQuery UI should really be on this list. Especially $.widget
  • json2 and closure compiler are used in every app I make...
  • Code formatting Guidelines... Best Friends
  • TESTING: It's not _part_ of your app, but FuncUnit seems the badassest
  • JavascriptMVC actually has a solution for ~everything I talked about, #js...
  • So does Dojo, for that matter...
  • That's ok though, as long as you know all the tools you have available

Promote JS - The BFF of JS Docs

promotejs

Your Best Friends?

  • Which of your best friends did I leave out?

With Best regards, for into the good future