Using jQuery Well

* Right arrow key to move forward; left arrow key to move backward. Or, use slide list at bottom of page.

Karl Swedberg

yepnope.js Example

Works great with Modernizr (www.modernizr.com)

  yepnope([{
    test : Modernizr.geolocation,
    yep  : 'normal.js',
    nope : ['polyfill.js', 'wrapper.js']
  }]);

yepnope.js Example

  yepnope([{
    load: '//ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js',
    complete: function () {
      if (!window.jQuery) {
        yepnope('local/jquery.min.js');
      }
    }
  }, {
    load: 'jquery.plugin.js',
    complete: function () {
      jQuery(document).ready(function($) {
          $('div').plugin();
      });
    }
  }]);

yepnope.js now in Modernizr!

jQuery is JavaScript

JavaScript is like your little sister, the art school student.

jQuery is JavaScript

jQuery is your little sister's extreme makeover.

Messy

Quirky

White Space Doesn't Matter

var fish = {
  one  :  'red'
  , two  :  'blue' ,
  three: 'Lucy'
};
$('div').
addClass(fish.one)
  .    removeClass(
          fish.two );


Quirky

White Space Doesn't Matter — Unless It Does.

// Bad (returns undefined):
return
{
  myName: 'Karl'
};

// Good (returns an object):
return {
  myName: 'Karl'
};

Quirky

Loose typing is fun — and frustrating.

  '' == 0   // true
  0 == '0'  // true
  '' == '0' // false
  false == 'false' // false
  false == '0' // true
  false == undefined // false
  false == null  // false
  null == undefined  // true
  typeof null // "object"
  typeof undefined // "undefined"

Quirky

Loose typing is fun — and frustrating.

'' === 0   // false
0 === '0'  // false
'' === '0' // false
false === 'false' // false
false === '0' // false
false === undefined // false
false === null  // false
null === undefined  // false

Beautiful

Metamorphosis!

  if ( !(window.matchMedia) ) {
    window.matchMedia = (function(doc,undefined) {
      // ... prep work ... do this stuff once, immediately

      return function(q){
        // ... other stuff ...
        // do this each time matchMedia called
      };
    })(document);
  }

Beautiful

Functions can return functions

function multiple(n) {
  function f(x) {
    return x * n;
  }
  return f;
}
var triple = multiple(3);
var quadruple = multiple(4);

console.log( triple(5) ); // 15
console.log( quadruple(5) ); // 20
console.log( multiple(4)(5) ); // 20

jQuery Makes JavaScript More Beautifuller

$

$ == jQuery

$( )

$() == jQuery()

$( 'div' )

$( '<div/>' )

$('<div id="rover" class="dog"><div class="tail">wag</div></div>')
.appendTo('body')
.bind('click', function() {
  $(this).find('div.tail').effect('shake');
})
.trigger('click');

$( '<div/>' )

$('<div id="rover" class="dog"></div>')
.append('<div class="tail">wag</div>')
.appendTo('body')
.bind('click', function() {
  $(this).find('div.tail').effect('shake');
})
.trigger('click');

$( '<div/>' )

$('<div></div>', {
  id: 'rover',
  'class': 'dog',
  html: '<div class="tail">wag</div>',
  click: function() {
    $(this).find('div.tail').effect('shake');
  }
}).appendTo('body')
.trigger('click');

$( '<div/>' )

var dog = {
  id: 'rover',
  'class': 'dog',
  html: '<div class="tail">wag</div>',
  click: function() {
    $(this).effect('shake');
  }
};

$('<div></div>', dog)
.appendTo('body').click();

Selector Tips

var $listItems = $('li');
var numItems = $listItems.length

//no need for length check
$listItems.addClass('pretty');  

if (numItems) {
  // do something with other elements
}

Selector Tips

$('#menu li').each(function(index) {  
  $(this).click(function() {
    
    $('#footer li:eq(' + index + ')')
    .toggleClass('active');
  });
});

Avoid Custom Selectors When You Can

...like the one in the previous slide.

Alternatives to Custom Selectors

DOM or jQuery

DOM or jQuery: Examples

$('#nav a').each(function(index) {
  // DOM
  var thisId = this.id; // good
  var otherId = $(this).prop('id'); // unnecessary

  // jQuery
  var thisHref = this.href; // unreliable
  var otherHref = $(this).attr('href'); // better
  
  // both
  $(this).prop('id', this.id + index).addClass('indexed');
});
    

Anonymous Functions

  var foo = function() { /* do something */ };

  $('div.reckless').slideToggle(300, function() {
    /* do something */
  });

  $('div.endangerment').slideToggle(150, foo);

Immediately Invoked Function Expression

IIFE

IIFE Example

  var KS = {};

  (function() {
    var foo = 'bar';
    KS.foo = 'table';
  })();
  // foo = undefined;
  // KS.foo == 'table';
  

Constructor Function

  var KS = function(arg) {
    if ( !(this instanceof KS) ) {
      return new KS(arg);
    }
    // do something with arg

    this.arg = arg;
    return this;
  };

  KS.prototype.foo = function(bar) {
   // do something
   return this;
  };

  KS.foo = function(bar) {
    return (bar || 1) * 2;
  }

jQuery Magic

var jQuery = function( selector, context ) {
  // The jQuery object is actually just the init constructor 'enhanced'
  return new jQuery.fn.init( selector, context, rootjQuery );
};

jQuery Magic

  jQuery.fn = jQuery.prototype = {
    constructor: jQuery,
    init: function( selector, context, rootjQuery ) {
      var match, elem, ret, doc;

      // Handle $(""), $(null), or $(undefined)
      if ( !selector ) {
        return this;
      }

      // Handle $(DOMElement)
      if ( selector.nodeType ) {
        this.context = this[0] = selector;
        this.length = 1;
        return this;
      }
      // handle all sorts of other stuff
  };
  

Event Delegation

“Live” Events

$('tr')
  .bind('click', function() {
    $(this).css({color: 'red'});
  })
  .live('click', function() {
    $(this).css({backgroundColor: 'yellow'});
  });
heading another yet another
data more data even more
data more data even more

Live in your head

// live actually binds to the document element
// and it's always available, even in the head
$('a.special').live('click', function(event) {
  event.preventDefault();
  $('body').append('I think you are special, too!');
});

Delegate with precision

Delegate example

$('#dwrapper').delegate('span', 'click', colormeblue);
$('#ddiv').delegate('span', 'click', mybgyellow);

$('#dbtn').bind('click', injectddiv);
#dwrapper
#ddiv span

Effects

Callback Gotchas

Unexpected Callback Demo

hi
hi

Use a Promise

$('button').bind('click', function() { $('div.pcb').slideToggle(400).promise().done(function() { // do something after all div.pcb elements have finished sliding }); });

Promise Callback Demo

hi
hi

Callback Tip

  var myCallback = function() {
    // I'mma gonna do something
  }
  function yourCallback() {
    // You do something, too!
  };

  $('.mydiv').slideUp(250, myCallback);
  $('.yourdiv').slideUp(250, yourCallback);
  

Callbackitis

The Problem:

The Evidence:

$('.mydiv').eq(0).fadeOut('slow', function() {
  $('.mydiv').eq(1).fadeOut('slow', function() {
    $('.mydiv').eq(2).fadeOut('slow', function() {
      $('.mydiv').eq(3).fadeOut('slow', function() {
        $('.mydiv').eq(4).fadeOut('slow', function() {
          // abandon all hope
        });
      });
    });
  });
});

Callbackitis

The Solution:

Repeating Callbacks

var $sequence = $('div.sequence'),
      index = 0;

  (function sequencer() {
      $sequence.eq(index++)
      .animate({opacity: 'toggle'}, 'slow', sequencer);
  })();

Repeating Callbacks Demo

10
9
8
7
6
5
4
3
2
1
Blast off!

More Repeating Callbacks

var $pulse = $('div.pulse'),
      index = 0,
      total = 10;

  (function pulse() {
    if (index++ < total) {
      $pulse
      .animate({opacity: 'toggle'}, pulse);
    }
  })();

More Repeating Callbacks Demo

10
9
8
7
6
5
4
3
2
1

Pulsee Plugin!

$.fn.pulsee = function(options) {
    var counter = 0, self = this;
    var opts = $.extend({}, $.fn.pulsee.defaults,
      typeof options === 'number' ? {total: options} : options || {}
    );

    (function pulse() {
      if (counter++ < opts.total) {
        self.animate({opacity: 'toggle'}, opts.duration, opts.easing,
          function() {
            opts.complete.call(this, counter-1);
            pulse();
          }
        );
      }
    })();

    return self;
  };
  $.fn.pulsee.defaults = {total: 10, complete: $.noop};

Pulsee Plugin Demo!

  var colors = ['#900', '#090', '#009', '#cc0', '#0cc', '#c0c'];
  var pulseOpts = {
    total: 9,
    duration: 489,
    complete: function(i) {
      this.style.backgroundColor = colors[i % 6];
    }
  };
  $('#pulseer').click(function() {
    $('div.pulsee').slice(0,5).pulsee(11);
    $('div.pulsee').slice(5).pulsee(pulseOpts);
  });
  

1
2
3
4
5
6
7
8
9
10

More Promises: Now with Ajax!

Ajax: The Old Way

$.ajax({
  url: 'http://api.jquery.com/jsonp/',
  dataType: 'jsonp',
  data: {
    title: search
  },
  timeout: 15000,
  success: successFn,
  error: errorFn,
  complete: completeFn
});

Ajax: The New Way

As of jQuery 1.5 (deferred.always() as of 1.6)

$.ajax({
  url: 'http://api.jquery.com/jsonp/',
  dataType: 'jsonp',
  data: {
    title: search
  },
  timeout: 15000
})
.done(successFn)
.fail(errorFn)
.always(completeFn);

Ajax: The New Way 2

var request = $.ajax({
  url: 'http://api.jquery.com/jsonp/',
  dataType: 'jsonp',
  data: {
    title: search
  },
  timeout: 15000
});

request.done(successFn);
request.fail(errorFn);
request.always(completeFn);

Ajax: Multiple Promise handlers

request.done(successFnA, successFnB, successFnC);
request.done([successFnD, successFnE, successFnF]);
request.done(successFnG).done(successFnH).done(successFnJ);

Caching Ajax

var api = {}, $response = $('#response');
$('#ajaxForm').bind('submit', function(event) {
  event.preventDefault();
  var search = $('#title').val();
  $response.empty().addClass('loading');

  if (!api[search]) {
    api[search] = $.ajax({
      url: 'http://api.jquery.com/jsonp/',
      dataType: 'jsonp',
      data: {
        title: search
      },
      timeout: 15000
    });
  }
  api[search].done(successFn).fail(errorFn).always(completeFn);
});

The End

Thanks for listening!

Any questions?

Check out the presentation again at pres.learningjquery.com/annarbor/

Contact: