jQuery Pluginization

Basic Functionality

(This is not very jQuery-like)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function longUrl( elem ) {
  var href = elem.attr( "href" ),
      api = "http://www.longurlplease.com/api/v1.1?callback=?";
   
  // Fetch JSON data.
  jQuery.getJSON( api, { q: href }, function(data){
    if ( data[ href ] ) {
      // Update the element's title attribute.
      elem.attr( "title", data[ href ] );
    }
  });
};
 
// This is no fun!
longUrl( $("a") );
 
// Wouldn't you much rather do this?
$("a").longUrl();

More jQuery-Like

(Implicit iteration, chaining, amazing!)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
jQuery.fn.longUrl = function() {
  return this.each(function(){
    var elem = jQuery(this),
        href = elem.attr( "href" ),
        api = "http://www.longurlplease.com/api/v1.1?callback=?";
     
    // Fetch JSON data.
    jQuery.getJSON( api, { q: href }, function(data){
      if ( data[ href ] ) {
        // If the data is valid, update the element's title attribute.
        elem.attr( "title", data[ href ] );
      }
    });
  });
};
 
// So much better!
$("a").longUrl().addClass( "yay-i-am-totally-chainable" );

Consider Performance

(Don't over-optimize, just optimize)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Store fetched long URLs here.
var cache = {};
 
jQuery.fn.longUrl = function() {
  return this.each(function(){
    var elem = jQuery(this),
        href = elem.attr( "href" ),
        api = "http://www.longurlplease.com/api/v1.1?callback=?";
     
    if ( cache[ href ] ) {
      // URL exists in cache, so use it.
      elem.attr( "title", cache[ href ] );
       
    } else {
      // Fetch JSON data.
      jQuery.getJSON( api, { q: href }, function(data){
        if ( data[ href ] ) {
          // If the data is valid, update the cache.
          cache[ href ] = data[ href ];
           
          // Update the element's title attribute.
          elem.attr( "title", cache[ href ] );
        }
      });
    }
  });
};

Use a Closure!

(Less global vars == less compatibility headaches)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
(function($){
   
  // Store fetched long URLs here.
  var cache = {};
   
  $.fn.longUrl = function() {
    return this.each(function(){
      var elem = $(this),
          href = elem.attr( "href" ),
          api = "http://www.longurlplease.com/api/v1.1?callback=?";
       
      if ( cache[ href ] ) {
        // URL exists in cache, so use it.
        elem.attr( "title", cache[ href ] );
         
      } else {
        // Fetch JSON data.
        $.getJSON( api, { q: href }, function(data){
          if ( data[ href ] ) {
            // If the data is valid, update the cache.
            cache[ href ] = data[ href ];
 
            // Update the element's title attribute.
            elem.attr( "title", cache[ href ] );
          }
        });
      }
    });
  };
   
})(jQuery);

Keep it DRY

(Why write something twice, when you can write it once?)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
(function($){
   
  // Store fetched long URLs here.
  var cache = {};
   
  $.fn.longUrl = function() {
    return this.each(function(){
      var elem = $(this),
          href = elem.attr( "href" ),
          api = "http://www.longurlplease.com/api/v1.1?callback=?";
       
      // Lengthen the element's URL.
      function lengthen( url ) {
        elem.attr( "title", url );
      };
       
      if ( cache[ href ] ) {
        // URL exists in cache, so use it.
        lengthen( cache[ href ] );
         
      } else {
        // Fetch JSON data.
        $.getJSON( api, { q: href }, function(data){
          if ( data[ href ] ) {
            // If the data is valid, update the cache.
            cache[ href ] = data[ href ];
 
            // Update the element's title attribute.
            lengthen( cache[ href ] );
          }
        });
      }
    });
  };
   
})(jQuery);

Options

(Sensible parameterization == less editing, down the road)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
(function($){
   
  // Store fetched long URLs here.
  var cache = {};
   
  $.fn.longUrl = function( attr ) {
    return this.each(function(){
      var elem = $(this),
          url = elem.attr( attr || "href" ),
          api = "http://www.longurlplease.com/api/v1.1?callback=?";
       
      // Lengthen the element's URL.
      function lengthen( url ) {
        elem.attr( "title", url );
      };
       
      if ( cache[ url ] ) {
        // URL exists in cache, so use it.
        lengthen( cache[ url ] );
         
      } else {
        // Fetch JSON data.
        $.getJSON( api, { q: url }, function(data){
          if ( data[ url ] ) {
            // If the data is valid, update the cache.
            cache[ url ] = data[ url ];
 
            // Update the element's title attribute.
            lengthen( cache[ url ] );
          }
        });
      }
    });
  };
   
})(jQuery);

More Options

(There must be a better way than this)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
(function($){
   
  // Store fetched long URLs here.
  var cache = {};
   
  $.fn.longUrl = function( attr, lengthen ) {
    // If only one argument is passed, and that argument is a function,
    // it must be `lengthen, so `attr` must have been omitted.
    if ( $.isFunction( attr ) ) {
      lengthen = attr;
      attr = null;
    }
     
    // If lengthen is not set, use a default function.
    lengthen = lengthen || function( elem, url ) {
      elem.attr( "title", url );
    };
     
    return this.each(function(){
      var elem = $(this),
          url = elem.attr( attr || "href" ),
          api = "http://www.longurlplease.com/api/v1.1?callback=?";
       
      if ( cache[ url ] ) {
        // URL exists in cache, so use it.
        lengthen( elem, cache[ url ] );
         
      } else {
        // Fetch JSON data.
        $.getJSON( api, { q: url }, function(data){
          if ( data[ url ] ) {
            // If the data is valid, update the cache.
            cache[ url ] = data[ url ];
             
            // Update the element's title attribute.
            lengthen( elem, cache[ url ] );
          }
        });
      }
    });
  };
   
})(jQuery);

Better Options

(This is the better way)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
(function($){
   
  // Store fetched long URLs here.
  var cache = {};
   
  $.fn.longUrl = function( options ) {
    // Some sensible defaults.
    var defaults = {
      attr: "href",
      lengthen: function( elem, url ) {
        elem.attr( "title", url );
      }
    };
     
    // Override defaults with specified options.
    options = $.extend( {}, defaults, options );
     
    return this.each(function(){
      var elem = $(this),
          url = elem.attr( options.attr ),
          api = "http://www.longurlplease.com/api/v1.1?callback=?";
       
      if ( cache[ url ] ) {
        // URL exists in cache, so use it.
        options.lengthen( elem, cache[ url ] );
         
      } else {
        // Fetch JSON data.
        $.getJSON( api, { q: url }, function(data){
          if ( data[ url ] ) {
            // If the data is valid, update the cache.
            cache[ url ] = data[ url ];
             
            // Update the element's title attribute.
            options.lengthen( elem, cache[ url ] );
          }
        });
      }
    });
  };
   
})(jQuery);

Best Options!

(Options can be overriden both globally and per-call)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
(function($){
   
  // Store fetched long URLs here.
  var cache = {};
   
  $.fn.longUrl = function( options ) {
    // Override defaults with specified options.
    options = $.extend( {}, $.fn.longUrl.options, options );
     
    return this.each(function(){
      var elem = $(this),
          url = elem.attr( options.attr ),
          api = "http://www.longurlplease.com/api/v1.1?callback=?";
       
      if ( cache[ url ] ) {
        // URL exists in cache, so use it.
        options.lengthen( elem, cache[ url ] );
         
      } else {
        // Fetch JSON data.
        $.getJSON( api, { q: url }, function(data){
          if ( data[ url ] ) {
            // If the data is valid, update the cache.
            cache[ url ] = data[ url ];
             
            // Update the element's title attribute.
            options.lengthen( elem, cache[ url ] );
          }
        });
      }
    });
  };
   
  // Some sensible defaults.
  $.fn.longUrl.options = {
    attr: "href",
    lengthen: function( elem, url ) {
      elem.attr( "title", url );
    }
  };
   
})(jQuery);

Even more jQuery-Like

(How can the lengthen callback be improved?)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
(function($){
   
  // Store fetched long URLs here.
  var cache = {};
   
  $.fn.longUrl = function( options ) {
    // Override defaults with specified options.
    options = $.extend( {}, $.fn.longUrl.options, options );
     
    return this.each(function(){
      var that = this,
          url = $(this).attr( options.attr ),
          api = "http://www.longurlplease.com/api/v1.1?callback=?";
       
      if ( cache[ url ] ) {
        // URL exists in cache, so use it.
        options.lengthen.call( that, cache[ url ] );
         
      } else {
        // Fetch JSON data.
        $.getJSON( api, { q: url }, function(data){
          if ( data[ url ] ) {
            // If the data is valid, update the cache.
            cache[ url ] = data[ url ];
             
            // Update the element's title attribute.
            options.lengthen.call( that, cache[ url ] );
          }
        });
      }
    });
  };
   
  // Some sensible defaults.
  $.fn.longUrl.options = {
    attr: "href",
    lengthen: function( url ) {
      $(this).attr( "title", url );
    }
  };
   
})(jQuery);

Even more Generalized

(The only thing left out is $.kitchenSink)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
(function($){
   
  // Store fetched long URLs here.
  var cache = {};
   
  $.longUrl = function( url, callback ) {
    var api = "http://www.longurlplease.com/api/v1.1?callback=?";
     
    if ( cache[ url ] ) {
      // URL exists in cache, so use it.
      callback( cache[ url ] );
       
    } else {
      // Fetch JSON data.
      $.getJSON( api, { q: url }, function(data){
        if ( data[ url ] ) {
          // If the data is valid, update the cache.
          cache[ url ] = data[ url ];
           
          // Call `callback` for this element + long url.
          callback( cache[ url ] );
        }
      });
    }
  }
   
  $.fn.longUrl = function( options ) {
    // Override defaults with specified options.
    options = $.extend( {}, $.fn.longUrl.options, options );
     
    return this.each(function(){
      var that = this,
          url = $(this).attr( options.attr );
       
      // Fetch long URL and call `lengthen` when done.
      $.longUrl( url, function( long_url ){
        options.lengthen.call( that, long_url );
      });
    });
  };
   
  // Some sensible defaults.
  $.fn.longUrl.options = {
    attr: "href",
    lengthen: function( url ) {
      $(this).attr( "title", url );
    }
  };
   
})(jQuery);

Recap